Accept Merge Request #269 修复部分逻辑问题 : (virusdefender-dev -> dev)

Merge Request: 修复部分逻辑问题
Created By: @virusdefender
Accepted By: @virusdefender
URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/269
This commit is contained in:
virusdefender 2015-10-08 11:14:41 +08:00
commit 807e0ed6ff
25 changed files with 287 additions and 48 deletions

View File

@ -120,6 +120,9 @@ class ContestRank(models.Model):
# key 是比赛题目的id
submission_info = JSONField(default={})
class Meta:
db_table = "contest_rank"
def update_rank(self, submission):
if not submission.contest_id or submission.contest_id != self.contest_id:
raise ValueError("Error submission type")

View File

@ -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,

View File

@ -37,5 +37,6 @@ class JoinGroupRequest(models.Model):
# 是否处理
status = models.BooleanField(default=False)
accepted = models.BooleanField(default=False)
class Meta:
db_table = "join_group_request"

View File

@ -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) + \

View File

@ -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" % \

View File

@ -39,20 +39,21 @@ class MessageQueue(object):
logger.warning("Submission user does not exist, submission_id: " + submission_id)
continue
if submission.result == result["accepted"] and not submission.contest_id:
if not submission.contest_id:
# 更新普通题目的 ac 计数器
try:
problem = Problem.objects.get(id=submission.problem_id)
problem.total_accepted_number += 1
problem.save()
except Problem.DoesNotExist:
logger.warning("Submission problem does not exist, submission_id: " + submission_id)
continue
if submission.result == result["accepted"]:
try:
problem = Problem.objects.get(id=submission.problem_id)
problem.total_accepted_number += 1
problem.save()
except Problem.DoesNotExist:
logger.warning("Submission problem does not exist, submission_id: " + submission_id)
continue
problems_status = user.problems_status
problems_status["problems"][str(problem.id)] = 1
user.problems_status = problems_status
user.save()
problems_status = user.problems_status
problems_status["problems"][str(problem.id)] = 1
user.problems_status = problems_status
user.save()
# 普通题目的话,到这里就结束了
continue

View File

@ -45,6 +45,7 @@ class AbstractProblem(models.Model):
total_accepted_number = models.IntegerField(default=0)
class Meta:
db_table = "problem"
abstract = True

View File

@ -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,

View File

@ -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) {

View File

@ -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("题目添加成功!题目现在处于隐藏状态,请到题目列表手动修改,并添加分类和难度信息!");

View File

@ -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("题目编辑成功!");

View File

@ -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);

View File

@ -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("题目添加成功!");

View File

@ -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("题目编辑成功!");

View File

@ -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);

View File

@ -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;

View File

@ -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",

View File

@ -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",

View File

@ -1 +1 @@
require(["jquery", "modal", "dropdown", "transition"]);
require(["jquery", "modal", "dropdown", "transition", "collapse"]);

View 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);
});

View File

@ -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 %}

View File

@ -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>

View File

@ -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>

View File

@ -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"]:

View File

@ -38,7 +38,7 @@ class XssHtml(HTMLParser):
'p', 'div', 'em', 'span', 'h1', 'h2', 'h3', 'h4',
'h5', 'h6', 'blockquote', 'ul', 'ol', 'tr', 'th', 'td',
'hr', 'li', 'u', 'embed', 's', 'table', 'thead', 'tbody',
'caption', 'small', 'q', 'sup', 'sub']
'caption', 'small', 'q', 'sup', 'sub', 'font']
common_attrs = ["style", "class", "name"]
nonend_tags = ["img", "hr", "br", "embed"]
tags_own_attrs = {
@ -46,6 +46,7 @@ class XssHtml(HTMLParser):
"a": ["href", "target", "rel", "title"],
"embed": ["src", "width", "height", "type", "allowfullscreen", "loop", "play", "wmode", "menu"],
"table": ["border", "cellpadding", "cellspacing"],
"font": ["color"]
}
def __init__(self, allows=[]):