Merge branch 'virusdefender-dev'
* virusdefender-dev: bootstrap 更新打包 require.js 加载的 js 也加版本号 修复导航栏在小屏幕上折叠后无法展开的问题 增加比赛结果缓存 优化比赛的前台显示样式;删除公告,没有提交的时候不显示表格的表头;修改部分 typo 增强判题和 docker 安全性 使用关联查询提供性能 修复 ajax 提交 json 中文乱码的问题 update security 修复不能显示真实姓名的 bug
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# coding=utf-8
|
||||
import json
|
||||
import datetime
|
||||
import redis
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.db import IntegrityError
|
||||
@@ -8,6 +9,7 @@ from django.utils import dateparse
|
||||
from django.db.models import Q, Sum
|
||||
from django.core.paginator import Paginator
|
||||
from django.utils.timezone import now
|
||||
from django.conf import settings
|
||||
|
||||
from rest_framework.views import APIView
|
||||
|
||||
@@ -334,7 +336,7 @@ def contest_problems_list_page(request, contest_id):
|
||||
比赛所有题目的列表页
|
||||
"""
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
contest_problems = ContestProblem.objects.filter(contest=contest).order_by("sort_index")
|
||||
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,
|
||||
"contest": {"id": contest_id}})
|
||||
|
||||
@@ -384,7 +386,18 @@ def contest_list_page(request, page=1):
|
||||
def contest_rank_page(request, contest_id):
|
||||
contest = Contest.objects.get(id=contest_id)
|
||||
contest_problems = ContestProblem.objects.filter(contest=contest).order_by("sort_index")
|
||||
rank = ContestRank.objects.filter(contest_id=contest_id).order_by("-total_ac_number", "total_time")
|
||||
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)
|
||||
return render(request, "oj/contest/contest_rank.html",
|
||||
{"rank": rank, "contest": contest,
|
||||
"contest_problems": contest_problems,
|
||||
|
||||
@@ -62,6 +62,8 @@ class JudgeClient(object):
|
||||
" --max-real-time " + str(self._max_real_time / 1000.0 * 2) + \
|
||||
" --max-memory " + str(self._max_memory * 1000 * 1000) + \
|
||||
" --network false" + \
|
||||
" --remount-dev true " + \
|
||||
" --reset-env true " + \
|
||||
" --syscalls '" + self._language["syscalls"] + "'" + \
|
||||
" --max-nprocess 20" + \
|
||||
" --uid " + str(lrun_uid) + \
|
||||
|
||||
@@ -13,9 +13,10 @@ def judge(submission_id, time_limit, memory_limit, test_case_id):
|
||||
try:
|
||||
command = "%s run --privileged --rm " \
|
||||
"--link mysql " \
|
||||
"-v %s:/var/judger/test_case/ " \
|
||||
"-v %s:/var/judger/code/ " \
|
||||
"-v %s:/var/judger/test_case/:ro " \
|
||||
"-v %s:/var/judger/code/:ro " \
|
||||
"-v %s:/var/judger/code/log/ " \
|
||||
"--device /dev/null:/dev/null " \
|
||||
"%s " \
|
||||
"python judge/judger/run.py " \
|
||||
"--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % \
|
||||
|
||||
@@ -91,7 +91,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"],
|
||||
$.ajax({
|
||||
beforeSend: csrfTokenHeader,
|
||||
url: "/api/admin/announcement/",
|
||||
contentType: "application/json",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
dataType: "json",
|
||||
method: "put",
|
||||
data: JSON.stringify({
|
||||
@@ -209,7 +209,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"],
|
||||
$.ajax({
|
||||
beforeSend: csrfTokenHeader,
|
||||
url: "/api/admin/announcement/",
|
||||
contentType: "application/json",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
data: JSON.stringify({
|
||||
title: title,
|
||||
content: content,
|
||||
|
||||
@@ -46,7 +46,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
||||
beforeSend: csrfTokenHeader,
|
||||
url: "/api/admin/contest/",
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
data: JSON.stringify(ajaxData),
|
||||
method: "post",
|
||||
success: function (data) {
|
||||
|
||||
@@ -48,10 +48,9 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
|
||||
beforeSend: csrfTokenHeader,
|
||||
url: "/api/admin/contest/",
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
data: JSON.stringify(ajaxData),
|
||||
method: "put",
|
||||
contentType: "application/json",
|
||||
success: function (data) {
|
||||
if (!data.code) {
|
||||
bsAlert("修改成功!");
|
||||
@@ -237,7 +236,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
|
||||
dataType: "json",
|
||||
data: JSON.stringify(ajaxData),
|
||||
method: "post",
|
||||
contentType: "application/json",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
success: function (data) {
|
||||
if (!data.code) {
|
||||
bsAlert("题目添加成功!题目现在处于隐藏状态,请到题目列表手动修改,并添加分类和难度信息!");
|
||||
|
||||
@@ -66,7 +66,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
dataType: "json",
|
||||
data: JSON.stringify(ajaxData),
|
||||
method: method,
|
||||
contentType: "application/json",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
success: function (data) {
|
||||
if (!data.code) {
|
||||
bsAlert("题目编辑成功!");
|
||||
|
||||
@@ -45,7 +45,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
|
||||
url: "/api/admin/group_member/",
|
||||
method: "put",
|
||||
data: JSON.stringify({group_id: relation.group, members: [relation.user.id]}),
|
||||
contentType: "application/json",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
success: function (data) {
|
||||
vm.memberList.remove(relation);
|
||||
bsAlert(data.data);
|
||||
|
||||
@@ -59,7 +59,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
dataType: "json",
|
||||
data: JSON.stringify(ajaxData),
|
||||
method: "post",
|
||||
contentType: "application/json",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
success: function (data) {
|
||||
if (!data.code) {
|
||||
bsAlert("题目添加成功!");
|
||||
|
||||
@@ -60,7 +60,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
|
||||
dataType: "json",
|
||||
data: JSON.stringify(ajaxData),
|
||||
method: "put",
|
||||
contentType: "application/json",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
success: function (data) {
|
||||
if (!data.code) {
|
||||
bsAlert("题目编辑成功!");
|
||||
|
||||
@@ -9,14 +9,14 @@ require(["jquery", "csrfToken", "bsAlert"], function ($, csrfTokenHeader, bsAler
|
||||
}
|
||||
|
||||
var groupId = window.location.pathname.split("/")[2];
|
||||
data = {group_id: groupId,message:message}
|
||||
var data = {group_id: groupId,message:message};
|
||||
$.ajax({
|
||||
url: "/api/group_join/",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
beforeSend: csrfTokenHeader,
|
||||
data: JSON.stringify(data),
|
||||
contentType: "application/json",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
success: function (data) {
|
||||
if (data.code) {
|
||||
bsAlert(data.data);
|
||||
|
||||
@@ -222,7 +222,7 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert", "ZeroClipboard"],
|
||||
url: url,
|
||||
method: "post",
|
||||
data: JSON.stringify(data),
|
||||
contentType: "application/json",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
success: function (data) {
|
||||
if (!data.code) {
|
||||
submissionId = data.data.submission_id;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
modal: "lib/bootstrap/modal",
|
||||
dropdown: "lib/bootstrap/dropdown",
|
||||
transition: "lib/bootstrap/transition",
|
||||
collapse: "lib/bootstrap/collapse",
|
||||
|
||||
//百度webuploader -> uploader
|
||||
webUploader: "lib/webuploader/webuploader",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var require = {
|
||||
urlArgs: "v=2",
|
||||
// RequireJS 通过一个相对的路径 baseUrl来加载所有代码。baseUrl通常被设置成data-main属性指定脚本的同级目录。
|
||||
baseUrl: "/static/js/",
|
||||
paths: {
|
||||
@@ -38,6 +39,7 @@ var require = {
|
||||
modal: "lib/bootstrap/modal",
|
||||
dropdown: "lib/bootstrap/dropdown",
|
||||
transition: "lib/bootstrap/transition",
|
||||
collapse: "lib/bootstrap/collapse",
|
||||
|
||||
//百度webuploader -> uploader
|
||||
webUploader: "lib/webuploader/webuploader",
|
||||
|
||||
2
static/src/js/lib/bootstrap/bootstrap.js
vendored
2
static/src/js/lib/bootstrap/bootstrap.js
vendored
@@ -1 +1 @@
|
||||
require(["jquery", "modal", "dropdown", "transition"]);
|
||||
require(["jquery", "modal", "dropdown", "transition", "collapse"]);
|
||||
214
static/src/js/lib/bootstrap/collapse.js
Normal file
214
static/src/js/lib/bootstrap/collapse.js
Normal file
@@ -0,0 +1,214 @@
|
||||
define([ 'jquery', './transition' ], function ( jQuery ) {
|
||||
/* ========================================================================
|
||||
* Bootstrap: collapse.js v3.3.5
|
||||
* http://getbootstrap.com/javascript/#collapse
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
|
||||
// COLLAPSE PUBLIC CLASS DEFINITION
|
||||
// ================================
|
||||
|
||||
var Collapse = function (element, options) {
|
||||
this.$element = $(element)
|
||||
this.options = $.extend({}, Collapse.DEFAULTS, options)
|
||||
this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
|
||||
'[data-toggle="collapse"][data-target="#' + element.id + '"]')
|
||||
this.transitioning = null
|
||||
|
||||
if (this.options.parent) {
|
||||
this.$parent = this.getParent()
|
||||
} else {
|
||||
this.addAriaAndCollapsedClass(this.$element, this.$trigger)
|
||||
}
|
||||
|
||||
if (this.options.toggle) this.toggle()
|
||||
}
|
||||
|
||||
Collapse.VERSION = '3.3.5'
|
||||
|
||||
Collapse.TRANSITION_DURATION = 350
|
||||
|
||||
Collapse.DEFAULTS = {
|
||||
toggle: true
|
||||
}
|
||||
|
||||
Collapse.prototype.dimension = function () {
|
||||
var hasWidth = this.$element.hasClass('width')
|
||||
return hasWidth ? 'width' : 'height'
|
||||
}
|
||||
|
||||
Collapse.prototype.show = function () {
|
||||
if (this.transitioning || this.$element.hasClass('in')) return
|
||||
|
||||
var activesData
|
||||
var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
|
||||
|
||||
if (actives && actives.length) {
|
||||
activesData = actives.data('bs.collapse')
|
||||
if (activesData && activesData.transitioning) return
|
||||
}
|
||||
|
||||
var startEvent = $.Event('show.bs.collapse')
|
||||
this.$element.trigger(startEvent)
|
||||
if (startEvent.isDefaultPrevented()) return
|
||||
|
||||
if (actives && actives.length) {
|
||||
Plugin.call(actives, 'hide')
|
||||
activesData || actives.data('bs.collapse', null)
|
||||
}
|
||||
|
||||
var dimension = this.dimension()
|
||||
|
||||
this.$element
|
||||
.removeClass('collapse')
|
||||
.addClass('collapsing')[dimension](0)
|
||||
.attr('aria-expanded', true)
|
||||
|
||||
this.$trigger
|
||||
.removeClass('collapsed')
|
||||
.attr('aria-expanded', true)
|
||||
|
||||
this.transitioning = 1
|
||||
|
||||
var complete = function () {
|
||||
this.$element
|
||||
.removeClass('collapsing')
|
||||
.addClass('collapse in')[dimension]('')
|
||||
this.transitioning = 0
|
||||
this.$element
|
||||
.trigger('shown.bs.collapse')
|
||||
}
|
||||
|
||||
if (!$.support.transition) return complete.call(this)
|
||||
|
||||
var scrollSize = $.camelCase(['scroll', dimension].join('-'))
|
||||
|
||||
this.$element
|
||||
.one('bsTransitionEnd', $.proxy(complete, this))
|
||||
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
|
||||
}
|
||||
|
||||
Collapse.prototype.hide = function () {
|
||||
if (this.transitioning || !this.$element.hasClass('in')) return
|
||||
|
||||
var startEvent = $.Event('hide.bs.collapse')
|
||||
this.$element.trigger(startEvent)
|
||||
if (startEvent.isDefaultPrevented()) return
|
||||
|
||||
var dimension = this.dimension()
|
||||
|
||||
this.$element[dimension](this.$element[dimension]())[0].offsetHeight
|
||||
|
||||
this.$element
|
||||
.addClass('collapsing')
|
||||
.removeClass('collapse in')
|
||||
.attr('aria-expanded', false)
|
||||
|
||||
this.$trigger
|
||||
.addClass('collapsed')
|
||||
.attr('aria-expanded', false)
|
||||
|
||||
this.transitioning = 1
|
||||
|
||||
var complete = function () {
|
||||
this.transitioning = 0
|
||||
this.$element
|
||||
.removeClass('collapsing')
|
||||
.addClass('collapse')
|
||||
.trigger('hidden.bs.collapse')
|
||||
}
|
||||
|
||||
if (!$.support.transition) return complete.call(this)
|
||||
|
||||
this.$element
|
||||
[dimension](0)
|
||||
.one('bsTransitionEnd', $.proxy(complete, this))
|
||||
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)
|
||||
}
|
||||
|
||||
Collapse.prototype.toggle = function () {
|
||||
this[this.$element.hasClass('in') ? 'hide' : 'show']()
|
||||
}
|
||||
|
||||
Collapse.prototype.getParent = function () {
|
||||
return $(this.options.parent)
|
||||
.find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
|
||||
.each($.proxy(function (i, element) {
|
||||
var $element = $(element)
|
||||
this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
|
||||
}, this))
|
||||
.end()
|
||||
}
|
||||
|
||||
Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
|
||||
var isOpen = $element.hasClass('in')
|
||||
|
||||
$element.attr('aria-expanded', isOpen)
|
||||
$trigger
|
||||
.toggleClass('collapsed', !isOpen)
|
||||
.attr('aria-expanded', isOpen)
|
||||
}
|
||||
|
||||
function getTargetFromTrigger($trigger) {
|
||||
var href
|
||||
var target = $trigger.attr('data-target')
|
||||
|| (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
|
||||
|
||||
return $(target)
|
||||
}
|
||||
|
||||
|
||||
// COLLAPSE PLUGIN DEFINITION
|
||||
// ==========================
|
||||
|
||||
function Plugin(option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.collapse')
|
||||
var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
|
||||
if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
|
||||
if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
}
|
||||
|
||||
var old = $.fn.collapse
|
||||
|
||||
$.fn.collapse = Plugin
|
||||
$.fn.collapse.Constructor = Collapse
|
||||
|
||||
|
||||
// COLLAPSE NO CONFLICT
|
||||
// ====================
|
||||
|
||||
$.fn.collapse.noConflict = function () {
|
||||
$.fn.collapse = old
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
// COLLAPSE DATA-API
|
||||
// =================
|
||||
|
||||
$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
|
||||
var $this = $(this)
|
||||
|
||||
if (!$this.attr('data-target')) e.preventDefault()
|
||||
|
||||
var $target = getTargetFromTrigger($this)
|
||||
var data = $target.data('bs.collapse')
|
||||
var option = data ? 'toggle' : $this.data()
|
||||
|
||||
Plugin.call($target, option)
|
||||
})
|
||||
|
||||
}(jQuery);
|
||||
|
||||
});
|
||||
@@ -29,7 +29,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-9 col-lg-9">
|
||||
<div class="col-md-12 col-lg-12">
|
||||
<div>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
@@ -60,9 +60,7 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-lg-3">
|
||||
{% include "oj/announcement/_announcement_panel.html" %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
<th class="text-center">AC / 总提交</th>
|
||||
<th class="text-center">用时 + 罚时</th>
|
||||
{% for item in contest_problems %}
|
||||
<th class="text-center"><a
|
||||
href="/contest/{{ contest.id }}/problem/{{ item.id }}/">{{ item.sort_index }}</a>
|
||||
<th class="text-center">
|
||||
<a href="/contest/{{ contest.id }}/problem/{{ item.id }}/">{{ item.sort_index }}</a>
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
@@ -50,9 +50,9 @@
|
||||
<tr>
|
||||
<th scope="row">{{ forloop.counter }}</th>
|
||||
<td>
|
||||
{{ item.user.username }}
|
||||
{{ item.user__username }}
|
||||
{% if show_real_name %}
|
||||
({{ item.real_name }})
|
||||
({{ item.user__real_name }})
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ item.total_ac_number }} / {{ item.total_submission_number }}</td>
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
</ul>
|
||||
</div>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr class="" success>
|
||||
|
||||
{% if submissions %}
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>题目名称</th>
|
||||
<th>用户</th>
|
||||
@@ -65,7 +67,6 @@
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% if submissions %}
|
||||
<tbody>
|
||||
{% for item in submissions %}
|
||||
<tr>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# coding=utf-8
|
||||
import datetime
|
||||
from django.utils.timezone import now
|
||||
import json
|
||||
|
||||
|
||||
def get_contest_status(contest):
|
||||
@@ -34,10 +33,11 @@ def get_the_formatted_time(seconds):
|
||||
|
||||
|
||||
def get_submission_class(rank, problem):
|
||||
if str(problem.id) not in rank.submission_info:
|
||||
submission_info = json.loads(rank["submission_info"])
|
||||
if str(problem.id) not in submission_info:
|
||||
return ""
|
||||
else:
|
||||
submission = rank.submission_info[str(problem.id)]
|
||||
submission = submission_info[str(problem.id)]
|
||||
if submission["is_ac"]:
|
||||
_class = "alert-success"
|
||||
if submission["is_first_ac"]:
|
||||
@@ -48,10 +48,11 @@ def get_submission_class(rank, problem):
|
||||
|
||||
|
||||
def get_submission_content(rank, problem):
|
||||
if str(problem.id) not in rank.submission_info:
|
||||
submission_info = json.loads(rank["submission_info"])
|
||||
if str(problem.id) not in submission_info:
|
||||
return ""
|
||||
else:
|
||||
submission = rank.submission_info[str(problem.id)]
|
||||
submission = submission_info[str(problem.id)]
|
||||
if submission["is_ac"]:
|
||||
r = get_the_formatted_time(submission["ac_time"])
|
||||
if submission["error_number"]:
|
||||
|
||||
Reference in New Issue
Block a user