Merge branch 'dev-sxw' into virusdefender-dev
* dev-sxw: 添加到编辑比赛页的入口[CI SKIP] 修复重复进入页面导致小组列表不断增加的问题,每次先清空[CI SKIP] 修改一些小问题,主要是在非管理员用户时,会调用models的字典对象的get方法,实际上这个字典没有get方法,会引发500,修改成了判断比赛(或者比赛题目)的创建者是否等于request.user; 另外仿照problem添加了对单个比赛信息的GETapi 添加admin编辑比赛页面以及js[CI SKIP] 修改提交样式 调整顺序 更新 Revert "修改比赛单个题目列表的样式" 修改比赛单个题目列表的样式 update contest_problem.html update contest_problem.html 添加运行判体脚本[CI SKIP] 对盘题系统针对新测试服务器进行调整,该分支将不会并入主分支[CI SKIP] correct the path of test case in judger 原来的dockerimage里没有MySQL-python,所以又改了下[CI SKIP] 漏了一个import[CI SKIP] 这是为了能以root身份运行celery[CI SKIP] 为了在qduoj.cf上运行做了改动,因为判题和web都在一台机器上,这个docker image是从digitalOcean上download下来的[CI SKIP]
This commit is contained in:
@@ -90,8 +90,8 @@ class ContestAdminAPIView(APIView):
|
|||||||
try:
|
try:
|
||||||
# 超级管理员可以编辑所有的
|
# 超级管理员可以编辑所有的
|
||||||
contest = Contest.objects.get(id=data["id"])
|
contest = Contest.objects.get(id=data["id"])
|
||||||
if request.user.admin_type != SUPER_ADMIN:
|
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
|
||||||
contest = contest.get(created_by=request.user)
|
return error_response(u"无权访问!")
|
||||||
except Contest.DoesNotExist:
|
except Contest.DoesNotExist:
|
||||||
return error_response(u"该比赛不存在!")
|
return error_response(u"该比赛不存在!")
|
||||||
try:
|
try:
|
||||||
@@ -144,6 +144,18 @@ class ContestAdminAPIView(APIView):
|
|||||||
---
|
---
|
||||||
response_serializer: ContestSerializer
|
response_serializer: ContestSerializer
|
||||||
"""
|
"""
|
||||||
|
contest_id = request.GET.get("contest_id", None)
|
||||||
|
if contest_id:
|
||||||
|
try:
|
||||||
|
# 普通管理员只能获取自己创建的题目
|
||||||
|
# 超级管理员可以获取全部的题目
|
||||||
|
contest = Contest.objects.get(id=contest_id)
|
||||||
|
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
|
||||||
|
return error_response(u"题目不存在")
|
||||||
|
return success_response(ContestSerializer(contest).data)
|
||||||
|
except Contest.DoesNotExist:
|
||||||
|
return error_response(u"题目不存在")
|
||||||
|
|
||||||
if request.user.admin_type == SUPER_ADMIN:
|
if request.user.admin_type == SUPER_ADMIN:
|
||||||
contest = Contest.objects.all().order_by("-create_time")
|
contest = Contest.objects.all().order_by("-create_time")
|
||||||
else:
|
else:
|
||||||
@@ -171,8 +183,8 @@ class ContestProblemAdminAPIView(APIView):
|
|||||||
data = serializer.data
|
data = serializer.data
|
||||||
try:
|
try:
|
||||||
contest = Contest.objects.get(id=data["contest_id"])
|
contest = Contest.objects.get(id=data["contest_id"])
|
||||||
if request.user.admin_type != SUPER_ADMIN:
|
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
|
||||||
contest = contest.get(created_by=request.user)
|
return error_response(u"比赛不存在")
|
||||||
except Contest.DoesNotExist:
|
except Contest.DoesNotExist:
|
||||||
return error_response(u"比赛不存在")
|
return error_response(u"比赛不存在")
|
||||||
contest_problem = ContestProblem.objects.create(title=data["title"],
|
contest_problem = ContestProblem.objects.create(title=data["title"],
|
||||||
@@ -236,8 +248,8 @@ class ContestProblemAdminAPIView(APIView):
|
|||||||
if contest_problem_id:
|
if contest_problem_id:
|
||||||
try:
|
try:
|
||||||
contest_problem = ContestProblem.objects.get(id=contest_problem_id)
|
contest_problem = ContestProblem.objects.get(id=contest_problem_id)
|
||||||
if request.user.admin_type != SUPER_ADMIN:
|
if request.user.admin_type != SUPER_ADMIN and contest_problem.created_by != request.user:
|
||||||
contest_problem = contest_problem.get(created_by=request.user)
|
return error_response(u"比赛题目不存在")
|
||||||
return success_response(ContestProblemSerializer(contest_problem).data)
|
return success_response(ContestProblemSerializer(contest_problem).data)
|
||||||
except ContestProblem.DoesNotExist:
|
except ContestProblem.DoesNotExist:
|
||||||
return error_response(u"比赛题目不存在")
|
return error_response(u"比赛题目不存在")
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from celery import Celery
|
from celery import Celery, platforms
|
||||||
from .settings import redis_config
|
from .settings import redis_config
|
||||||
|
|
||||||
app = Celery("judge", broker='redis://%s:%s/%s' % (redis_config["host"], redis_config["port"], redis_config["db"]),
|
app = Celery("judge", broker='redis://%s:%s/%s' % (redis_config["host"], redis_config["port"], redis_config["db"]),
|
||||||
include=["judge.judger_controller.tasks"])
|
include=["judge.judger_controller.tasks"])
|
||||||
|
|
||||||
|
platforms.C_FORCE_ROOT =True
|
||||||
|
|||||||
1
runJudge.sh
Executable file
1
runJudge.sh
Executable file
@@ -0,0 +1 @@
|
|||||||
|
nohup celery -A judge.judger_controller worker -l DEBUG &
|
||||||
@@ -104,6 +104,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
|||||||
bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组");
|
bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
vm.allGroups = [];
|
||||||
for (var i = 0; i < data.data.length; i++) {
|
for (var i = 0; i < data.data.length; i++) {
|
||||||
var item = data.data[i];
|
var item = data.data[i];
|
||||||
item["isSelected"] = false;
|
item["isSelected"] = false;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
|
|||||||
|
|
||||||
editContest: function(contestId){
|
editContest: function(contestId){
|
||||||
avalon.vmodels.admin.contestId = contestId;
|
avalon.vmodels.admin.contestId = contestId;
|
||||||
// todo 修改template_url
|
avalon.vmodels.admin.template_url = "template/contest/edit_contest.html";
|
||||||
},
|
},
|
||||||
showContestProblems: function(contestId){
|
showContestProblems: function(contestId){
|
||||||
avalon.vmodels.admin.contestId = contestId;
|
avalon.vmodels.admin.contestId = contestId;
|
||||||
@@ -51,7 +51,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
|
|||||||
success: function (data) {
|
success: function (data) {
|
||||||
if (!data.code) {
|
if (!data.code) {
|
||||||
vm.contestList = data.data.results;
|
vm.contestList = data.data.results;
|
||||||
vm.announcementList = data.data.results;
|
|
||||||
avalon.vmodels.contestListPager.totalPage = data.data.total_page;
|
avalon.vmodels.contestListPager.totalPage = data.data.total_page;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
180
static/src/js/app/admin/contest/editContest.js
Normal file
180
static/src/js/app/admin/contest/editContest.js
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "datetimePicker",
|
||||||
|
"validator", "editorComponent"],
|
||||||
|
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
|
||||||
|
|
||||||
|
$("#edit-contest-form").validator().on('submit', function (e) {
|
||||||
|
if (!e.isDefaultPrevented()) {
|
||||||
|
e.preventDefault();
|
||||||
|
var ajaxData = {
|
||||||
|
id: avalon.vmodels.admin.contestId,
|
||||||
|
title: vm.title,
|
||||||
|
description: avalon.vmodels.contestDescriptionEditor.content,
|
||||||
|
contest_type: 0,
|
||||||
|
real_time_rank: vm.realTimeRank,
|
||||||
|
start_time: vm.startTime,
|
||||||
|
end_time: vm.endTime,
|
||||||
|
visible: vm.visible
|
||||||
|
};
|
||||||
|
|
||||||
|
var selectedGroups = [];
|
||||||
|
if (!vm.isGlobal) {
|
||||||
|
for (var i = 0; i < vm.allGroups.length; i++) {
|
||||||
|
if (vm.allGroups[i].isSelected) {
|
||||||
|
selectedGroups.push(vm.allGroups[i].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ajaxData.groups = selectedGroups;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (vm.password) {
|
||||||
|
ajaxData.password = vm.password;
|
||||||
|
ajaxData.contest_type = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ajaxData.contest_type = 1;
|
||||||
|
}
|
||||||
|
if (!vm.isGlobal && !selectedGroups.length) {
|
||||||
|
bsAlert("你没有选择参赛用户!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ajaxData.description.trim() == "") {
|
||||||
|
bsAlert("比赛描述不能为空!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/admin/contest/",
|
||||||
|
dataType: "json",
|
||||||
|
contentType: "application/json;charset=UTF-8",
|
||||||
|
data: JSON.stringify(ajaxData),
|
||||||
|
method: "put",
|
||||||
|
success: function (data) {
|
||||||
|
if (!data.code) {
|
||||||
|
bsAlert("修改成功!");
|
||||||
|
vm.showContestListPage();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bsAlert(data.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (avalon.vmodels.edit_contest)
|
||||||
|
var vm = avalon.vmodels.edit_contest;
|
||||||
|
else
|
||||||
|
var vm = avalon.define({
|
||||||
|
$id: "edit_contest",
|
||||||
|
title: "",
|
||||||
|
startTime: "",
|
||||||
|
endTime: "",
|
||||||
|
password: "",
|
||||||
|
isGlobal: true,
|
||||||
|
allGroups: [],
|
||||||
|
showGlobalViewRadio: true,
|
||||||
|
realTimeRank: true,
|
||||||
|
visible: false,
|
||||||
|
showContestListPage: function() {
|
||||||
|
avalon.vmodels.admin.template_url = "template/contest/contest_list.html";
|
||||||
|
},
|
||||||
|
|
||||||
|
contestDescriptionEditor: {
|
||||||
|
editorId: "contest-description-editor",
|
||||||
|
placeholder: "比赛介绍内容"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/user/",
|
||||||
|
method: "get",
|
||||||
|
dataType: "json",
|
||||||
|
success: function (data) {
|
||||||
|
if (!data.code) {
|
||||||
|
var admin_type = data.data.admin_type;
|
||||||
|
if (data.data.admin_type == 1) {
|
||||||
|
vm.isGlobal = false;
|
||||||
|
vm.showGlobalViewRadio = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/admin/group/",
|
||||||
|
method: "get",
|
||||||
|
dataType: "json",
|
||||||
|
success: function (data) {
|
||||||
|
if (!data.code) {
|
||||||
|
if (!data.data.length) {
|
||||||
|
if (admin_type != 2)
|
||||||
|
bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm.allGroups = [];
|
||||||
|
for (var i = 0; i < data.data.length; i++) {
|
||||||
|
var item = data.data[i];
|
||||||
|
item["isSelected"] = false;
|
||||||
|
vm.allGroups.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bsAlert(data.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/admin/contest/?contest_id=" + avalon.vmodels.admin.contestId,
|
||||||
|
method: "get",
|
||||||
|
dataType: "json",
|
||||||
|
success: function (data) {
|
||||||
|
if (data.code) {
|
||||||
|
bsAlert(data.data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var contest = data.data;
|
||||||
|
vm.title = contest.title;
|
||||||
|
avalon.vmodels.contestDescriptionEditor.content = contest.description;
|
||||||
|
vm.visible = contest.visible;
|
||||||
|
vm.realTimeRank = contest.real_time_rank;
|
||||||
|
vm.startTime = contest.start_time.substring(0, 16).replace("T", " ");
|
||||||
|
vm.endTime = contest.end_time.substring(0, 16).replace("T", " ");
|
||||||
|
if (contest.contest_type == 0) { //contest_type == 0, 小组内比赛
|
||||||
|
vm.isGlobal = false;
|
||||||
|
for (var i = 0; i < vm.allGroups.length; i++) {
|
||||||
|
vm.allGroups[i].isSelected = false;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < contest.groups.length; i++) {
|
||||||
|
var id = parseInt(contest.groups[i]);
|
||||||
|
console.log(id);
|
||||||
|
for (var index = 0; vm.allGroups[index]; index++) {
|
||||||
|
if (vm.allGroups[index].id == id) {
|
||||||
|
vm.allGroups[index].isSelected = true;
|
||||||
|
console.log(id+"asdf");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vm.isGlobal = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
avalon.scan();
|
||||||
|
|
||||||
|
$("#contest_start_time").datetimepicker({
|
||||||
|
format: "yyyy-mm-dd hh:ii",
|
||||||
|
minuteStep: 5,
|
||||||
|
weekStart: 1,
|
||||||
|
language: "zh-CN"
|
||||||
|
});
|
||||||
|
$("#contest_end_time").datetimepicker({
|
||||||
|
format: "yyyy-mm-dd hh:ii",
|
||||||
|
minuteStep: 5,
|
||||||
|
weekStart: 1,
|
||||||
|
language: "zh-CN"
|
||||||
|
});
|
||||||
|
});
|
||||||
107
template/src/admin/contest/edit_contest.html
Normal file
107
template/src/admin/contest/edit_contest.html
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<div ms-controller="edit_contest" class="col-md-9">
|
||||||
|
<form id="edit-contest-form">
|
||||||
|
<nav>
|
||||||
|
<ul class="pager">
|
||||||
|
<li class="previous" ms-click="showContestListPage()"><a href="javascript:void(0)"><span
|
||||||
|
aria-hidden="true">←</span> 返回</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<label>比赛名称</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" name="name" class="form-control" ms-duplex="title"
|
||||||
|
data-error="请填写比赛名称(名称不能超过50个字)" required>
|
||||||
|
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<label>说明</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<ms:editor $id="contestDescriptionEditor" config="contestDescriptionEditor"></ms:editor>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label>开始时间</label>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="start_time" id="contest_start_time"
|
||||||
|
ms-duplex="startTime" data-error="请填写比赛开始时间" required>
|
||||||
|
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label>结束时间</label>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="end_time" id="contest_end_time"
|
||||||
|
ms-duplex="endTime" data-error="请填写比赛结束时间" required>
|
||||||
|
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>比赛类型</label>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span ms-if="showGlobalViewRadio">
|
||||||
|
<label>
|
||||||
|
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">全局可见
|
||||||
|
</small>
|
||||||
|
</label>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<label>
|
||||||
|
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组内可见
|
||||||
|
</small>
|
||||||
|
</label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6" ms-visible="isGlobal">
|
||||||
|
<label>密码保护</label>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-md-12" ms-visible="!isGlobal">
|
||||||
|
<!-- radio 的value 没有用 但是没有的话,表单验证会出错-->
|
||||||
|
<div ms-repeat="allGroups" class="col-md-4">
|
||||||
|
<input type="checkbox" value="group_id" ms-duplex-checked="el.isSelected"> {{ el.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label>实时排名</label>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="text"><input type="checkbox" ms-duplex-checked="realTimeRank">
|
||||||
|
<small>不勾选则排名不更新,且只显示自己的提交。</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label>是否可见</label>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="text"><input type="checkbox" ms-duplex-checked="visible">
|
||||||
|
<small>可见</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<input type="submit" class="btn btn-success btn-lg" value="发布比赛">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/js/app/admin/contest/editContest.js"></script>
|
||||||
Reference in New Issue
Block a user