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
This commit is contained in:
virusdefender 2015-08-25 12:49:05 +08:00
commit 31356da9d1
26 changed files with 1427 additions and 813 deletions

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 = [
('contest', '0004_remove_contestproblem_difficulty'),
]
operations = [
migrations.AddField(
model_name='contestproblem',
name='score',
field=models.IntegerField(default=0),
),
]

View File

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('contest', '0005_contestsubmission'),
('contest', '0005_contestproblem_score'),
]
operations = [
]

View File

@ -55,6 +55,7 @@ class ContestProblem(AbstractProblem):
contest = models.ForeignKey(Contest) contest = models.ForeignKey(Contest)
# 比如A B 或者1 2 或者 a b 将按照这个排序 # 比如A B 或者1 2 或者 a b 将按照这个排序
sort_index = models.CharField(max_length=30) sort_index = models.CharField(max_length=30)
score = models.IntegerField(default=0)
class Meta: class Meta:
db_table = "contest_problem" db_table = "contest_problem"

View File

@ -70,11 +70,11 @@ class CreateContestProblemSerializer(serializers.Serializer):
time_limit = serializers.IntegerField() time_limit = serializers.IntegerField()
memory_limit = serializers.IntegerField() memory_limit = serializers.IntegerField()
hint = serializers.CharField(max_length=3000, allow_blank=True) hint = serializers.CharField(max_length=3000, allow_blank=True)
score = serializers.IntegerField(required=False, default=0)
sort_index = serializers.CharField(max_length=30) sort_index = serializers.CharField(max_length=30)
class ContestProblemSerializer(serializers.ModelSerializer): class ContestProblemSerializer(serializers.ModelSerializer):
class ContestSerializer(serializers.ModelSerializer): class ContestSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Contest model = Contest
@ -101,10 +101,9 @@ class EditContestProblemSerializer(serializers.Serializer):
hint = serializers.CharField(max_length=3000, allow_blank=True) hint = serializers.CharField(max_length=3000, allow_blank=True)
visible = serializers.BooleanField() visible = serializers.BooleanField()
sort_index = serializers.CharField(max_length=30) sort_index = serializers.CharField(max_length=30)
score = serializers.IntegerField(required=False, default=0)
class ContestPasswordVerifySerializer(serializers.Serializer): class ContestPasswordVerifySerializer(serializers.Serializer):
contest_id = serializers.IntegerField() contest_id = serializers.IntegerField()
password = serializers.CharField(max_length=30) password = serializers.CharField(max_length=30)

View File

@ -28,6 +28,9 @@ class ContestAdminAPITest(APITestCase):
self.group = Group.objects.create(name="group1", description="des0", self.group = Group.objects.create(name="group1", description="des0",
join_group_setting=0, visible=True, join_group_setting=0, visible=True,
admin=user2) admin=user2)
self.group2 = Group.objects.create(name="group2", description="des0",
join_group_setting=0, visible=True,
admin=user1)
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=2, show_rank=True, show_user_submission=True, contest_type=2, show_rank=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z", start_time="2015-08-15T10:00:00.000Z",
@ -78,7 +81,7 @@ class ContestAdminAPITest(APITestCase):
response = self.client.post(self.url, data=data) response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 0) self.assertEqual(response.data["code"], 0)
def test_group_contest_successfully(self): def test_group_contest_super_admin_successfully(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"title": "title3", "description": "description3", "mode": 1, "contest_type": 0, data = {"title": "title3", "description": "description3", "mode": 1, "contest_type": 0,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z", "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
@ -86,6 +89,14 @@ class ContestAdminAPITest(APITestCase):
response = self.client.post(self.url, data=data) response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 0) self.assertEqual(response.data["code"], 0)
def test_group_contest_admin_successfully(self):
self.client.login(username="test2", password="testbb")
data = {"title": "title6", "description": "description6", "mode": 2, "contest_type": 0,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "groups": [self.group.id], "visible": True}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 0)
def test_time_error(self): def test_time_error(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"title": "title2", "description": "description2", "mode": 1, "contest_type": 2, data = {"title": "title2", "description": "description2", "mode": 1, "contest_type": 2,
@ -141,6 +152,15 @@ class ContestAdminAPITest(APITestCase):
self.assertEqual(response.data["data"]["end_time"], "2015-08-15T13:00:00Z") self.assertEqual(response.data["data"]["end_time"], "2015-08-15T13:00:00Z")
self.assertEqual(response.data["data"]["visible"], False) self.assertEqual(response.data["data"]["visible"], False)
def test_edit_group_contest_unsuccessfully(self):
self.client.login(username="test2", password="testbb")
data = {"id": self.group_contest.id, "title": "titleyyy", "description": "descriptionyyyy", "mode": 1,
"contest_type": 0, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T13:00:00.000Z",
"groups": [self.group.id], "visible": False}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_edit_group_at_least_one(self): def test_edit_group_at_least_one(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"id": self.group_contest.id, "title": "titleyyy", "description": "descriptionyyyy", "mode": 1, data = {"id": self.group_contest.id, "title": "titleyyy", "description": "descriptionyyyy", "mode": 1,
@ -169,7 +189,8 @@ class ContestAdminAPITest(APITestCase):
def test_edit_global_contest_password_exists(self): def test_edit_global_contest_password_exists(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
data = {"id": self.global_contest.id, "title": "title0", "description": "description0", "mode": 1, "contest_type": 2, data = {"id": self.global_contest.id, "title": "title0", "description": "description0", "mode": 1,
"contest_type": 2,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z", "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "visible": True} "end_time": "2015-08-15T12:00:00.000Z", "visible": True}
response = self.client.put(self.url, data=data) response = self.client.put(self.url, data=data)
@ -189,6 +210,10 @@ class ContestAdminAPITest(APITestCase):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
self.assertEqual(self.client.get(self.url).data["code"], 0) self.assertEqual(self.client.get(self.url).data["code"], 0)
def test_get_data_successfully_by_normal_admin(self):
self.client.login(username="test2", password="testbb")
self.assertEqual(self.client.get(self.url).data["code"], 0)
def test_keyword_contest(self): def test_keyword_contest(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
response = self.client.get(self.url + "?visible=true") response = self.client.get(self.url + "?visible=true")
@ -209,24 +234,30 @@ class ContestProblemAdminAPItEST(APITestCase):
self.user = User.objects.create(username="test1", admin_type=SUPER_ADMIN) self.user = User.objects.create(username="test1", admin_type=SUPER_ADMIN)
self.user.set_password("testaa") self.user.set_password("testaa")
self.user.save() self.user.save()
self.user2 = User.objects.create(username="test2", admin_type=ADMIN)
self.user2.set_password("testaa")
self.user2.save()
self.user3 = User.objects.create(username="test3", admin_type=ADMIN)
self.user3.set_password("testaa")
self.user3.save()
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=2, show_rank=True, show_user_submission=True, contest_type=2, show_rank=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z", start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z", end_time="2015-08-15T12:00:00.000Z",
password="aacc", created_by=User.objects.get(username="test1")) password="aacc", created_by=User.objects.get(username="test1"))
self. contest_problem = ContestProblem.objects.create(title="titlex", self.contest_problem = ContestProblem.objects.create(title="titlex",
description="descriptionx", description="descriptionx",
input_description="input1_description", input_description="input1_description",
output_description="output1_description", output_description="output1_description",
test_case_id="1", test_case_id="1",
samples=json.dumps([{"input": "1 1", "output": "2"}]), samples=json.dumps([{"input": "1 1", "output": "2"}]),
time_limit=100, time_limit=100,
memory_limit=1000, memory_limit=1000,
hint="hint1", hint="hint1",
created_by=User.objects.get(username="test1"), created_by=User.objects.get(username="test1"),
contest=Contest.objects.get(title="titlex"), contest=Contest.objects.get(title="titlex"),
sort_index="a") sort_index="a")
# 以下是发布比赛题目的测试 # 以下是发布比赛题目的测试
def test_invalid_format(self): def test_invalid_format(self):
@ -311,6 +342,10 @@ class ContestProblemAdminAPItEST(APITestCase):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
self.assertEqual(self.client.get(self.url).data["code"], 0) self.assertEqual(self.client.get(self.url).data["code"], 0)
def test_get_data_unsuccessfully(self):
self.client.login(username="test1", password="testaa")
self.assertEqual(self.client.get(self.url+"?contest_id=12").data["code"], 1)
def test_keyword_contest(self): def test_keyword_contest(self):
self.client.login(username="test1", password="testaa") self.client.login(username="test1", password="testaa")
response = self.client.get(self.url + "?visible=true") response = self.client.get(self.url + "?visible=true")
@ -333,4 +368,33 @@ class ContestProblemAdminAPItEST(APITestCase):
response = self.client.get(self.url, data=data) response = self.client.get(self.url, data=data)
self.assertEqual(response.data["code"], 0) self.assertEqual(response.data["code"], 0)
def test_query_contest_problem_exists_by_contest_id(self):
self.client.login(username="test3", password="testaa")
response = self.client.get(self.url + "?contest_id=1")
self.assertEqual(response.data["code"], 0)
self.assertEqual(len(response.data["data"]), 0)
def test_query_contest_problem_exists_by_normal_admin(self):
self.client.login(username="test2", password="testaa")
data = {"contest_problem_id": self.contest_problem.id}
response = self.client.get(self.url, data=data)
self.assertEqual(response.data["code"], 0)
def test_edit_problem_unsuccessfully_can_not_access(self):
self.client.login(username="test2", password="testaa")
data = {"id": self.contest_problem.id,
"title": "title2222222",
"description": "description22222222",
"input_description": "input_description2",
"output_description": "output_description2",
"test_case_id": "1",
"source": "source1",
"samples": [{"input": "1 1", "output": "2"}],
"time_limit": "100",
"memory_limit": "1000",
"hint": "hint1",
"sort_index": "b",
"visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 1)

View File

@ -2,17 +2,18 @@
import json import json
import datetime import datetime
from functools import wraps from functools import wraps
from collections import OrderedDict
from django.utils.timezone import now from django.utils.timezone import now
from django.shortcuts import render from django.shortcuts import render
from django.db import IntegrityError from django.db import IntegrityError
from django.utils import dateparse from django.utils import dateparse
from django.db.models import Q from django.db.models import Q, Count, Sum
from django.core.paginator import Paginator from django.core.paginator import Paginator
from rest_framework.views import APIView from rest_framework.views import APIView
from utils.shortcuts import (serializer_invalid_response, error_response, from utils.shortcuts import (serializer_invalid_response, error_response,
success_response, paginate, rand_str, error_page) success_response, paginate, rand_str, error_page)
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN, User
from account.decorators import login_required 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
@ -279,15 +280,21 @@ def contest_problem_page(request, contest_id, contest_problem_id):
contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True) contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
except ContestProblem.DoesNotExist: except ContestProblem.DoesNotExist:
return error_page(request, u"比赛题目不存在") return error_page(request, u"比赛题目不存在")
warning = "您已经提交过本题的正确答案!"
show_warning = False show_warning = False
try: try:
submission = ContestSubmission.objects.get(user=request.user, contest=contest, problem=contest_problem) submission = ContestSubmission.objects.get(user=request.user, contest=contest, problem=contest_problem)
show_warning = submission.ac show_warning = submission.ac
except ContestSubmission.DoesNotExist: except ContestSubmission.DoesNotExist:
pass pass
return render(request, "oj/contest/contest_problem.html", {"contest_problem": contest_problem,
# 已经结束
if contest.status == -1:
show_warning = True
warning = "比赛已经结束!"
return render(request, "oj/contest/contest_problem.html", {"contest_problem": contest_problem, "contest": contest,
"samples": json.loads(contest_problem.samples), "samples": json.loads(contest_problem.samples),
"show_warning": show_warning}) "show_warning": show_warning, "warning": warning})
@check_user_contest_permission @check_user_contest_permission
@ -353,4 +360,55 @@ def contest_list_page(request, page=1):
def _cmp(x, y):
if x["total_ac"] > y["total_ac"]:
return 1
elif x["total_ac"] < y["total_ac"]:
return -1
else:
if x["total_time"] < y["total_time"]:
return 1
else:
return -1
@check_user_contest_permission
def contest_rank_page(request, contest_id):
result = ContestSubmission.objects.values("user_id").annotate(total_submit=Count("user_id"))
for i in range(0, len(result)):
# 这个人所有的提交
submissions = ContestSubmission.objects.filter(user_id=result[i]["user_id"])
result[i]["total_ac"] = submissions.filter(ac=True).count()
result[i]["user"] = User.objects.get(id=result[i]["user_id"])
result[i]["submissions"] = submissions.order_by("problem__sort_index")
result[i]["total_time"] = submissions.filter(ac=True).aggregate(total_time=Sum("total_time"))["total_time"]
print result
return render(request, "oj/contest/contest_rank.html")
#
#
# return
# contest = Contest.objects.get(id=contest_id)
# contest_submissions = ContestSubmission.objects.filter(contest=contest)
# result = {}
# # 先把数据按照用户id 为 key 整理一下
# # {1: {"submissions": [], "total_time": 0, "total_ac": 0}}
# for item in contest_submissions:
# if item.user.id not in contest_submissions:
# result[item.user.id] = {"user": {"id": item.user.id, "username": item.user.username,
# "real_name": item.user.real_name},
# "submissions": [], "total_time": 0, "total_ac": 0}
# result[item.user.id]["submissions"].append(ContestSubmissionSerializer(item).data)
# if item.ac:
# result[item.user.id]["total_time"] += item.total_time
# result[item.user.id]["total_ac"] += 1
# l = []
# for k, v in result.iteritems():
# l.append(v)
# print sorted(l, cmp=_cmp, reverse=True)

View File

@ -0,0 +1,5 @@
.group-tag {
padding-left: 5px; color: #46799b; background: #e0eaf1; white-space: nowrap;
overflow: hidden; cursor: pointer; border-radius: 2px 0 0 2px;
float: left; padding: 0 4px;box-sizing: border-box;list-style-type: none; margin: 5px;
}

View File

@ -75,6 +75,9 @@ define("admin", ["jquery", "avalon"], function ($, avalon) {
groupId: -1, groupId: -1,
problemId: -1, problemId: -1,
adminNavList: [], adminNavList: [],
$contestMode: -1,
$problemId: -1,
$contestId: -1,
hide_loading: function () { hide_loading: function () {
$("#loading-gif").hide(); $("#loading-gif").hide();
}, },
@ -123,6 +126,18 @@ define("admin", ["jquery", "avalon"], function ($, avalon) {
vm.template_url = "template/problem/submission_list.html"; vm.template_url = "template/problem/submission_list.html";
}); });
vm.$watch("showContestProblemPage", function (problemId, contestId, contestMode) {
vm.$problemId = problemId;
vm.$contestId = contestId;
vm.$contestMode = contestMode
vm.template_url = "template/contest/edit_problem.html";
});
vm.$watch("showContestListPage", function (problemId) {
vm.problemId = problemId;
vm.template_url = "template/contest/contest_list.html";
});
avalon.scan(); avalon.scan();
window.onhashchange = function () { window.onhashchange = function () {

View File

@ -0,0 +1,166 @@
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "datetimePicker",
"validator"],
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
//avalon.vmodels.add_contest = null;
$("#add-contest-form").validator().on('submit', function (e) {
if (!e.isDefaultPrevented()){
e.preventDefault();
var ajaxData = {
title: vm.title,
description: vm.description,
mode: vm.mode,
contest_type: 0,
show_rank: vm.showRank,
show_user_submission: vm.showSubmission,
start_time: vm.startTime,
end_time: vm.endTime,
visible: false
};
if (vm.choseGroupList.length == 0) {
bsAlert("你没有选择参赛用户!");
return false;
}
if (vm.choseGroupList[0].id == 0) { //everyone | public contest
if (vm.password) {
ajaxData.password = vm.password;
ajaxData.contest_type = 2;
}
else{
ajaxData.contest_type = 1;
}
}
else { // Add groups info
ajaxData.groups = [];
for (var i = 0; vm.choseGroupList[i]; i++)
ajaxData.groups.push(parseInt(vm.choseGroupList[i].id))
}
$.ajax({ // Add contest
beforeSend: csrfTokenHeader,
url: "/api/admin/contest/",
dataType: "json",
contentType: "application/json",
data: JSON.stringify(ajaxData),
method: "post",
contentType: "application/json",
success: function (data) {
if (!data.code) {
bsAlert("添加成功!将转到比赛列表页以便为比赛添加问题(注意比赛当前状态为:隐藏)");
vm.title = "";
vm.description = "";
vm.startTime = "";
vm.endTime = "";
vm.password = "";
vm.mode = "";
vm.showRank = false;
vm.showSubmission = false;
vm.group = "-1";
vm.groupList = [];
vm.choseGroupList = [];
vm.passwordUsable = false;
location.hash = "#contest/contest_list";
}
else {
bsAlert(data.data);
}
}
});
}
return false;
});
editor("#editor");
if (avalon.vmodels.add_contest)
var vm = avalon.vmodels.add_contest;
else
var vm = avalon.define({
$id: "add_contest",
title: "",
description: "",
startTime: "",
endTime: "",
password: "",
mode: "",
showRank: false,
showSubmission: false,
group: "-1",
groupList: [],
choseGroupList: [],
passwordUsable: false,
addGroup: function() {
if (vm.group == -1) return;
if (vm.groupList[vm.group].id == 0){
vm.passwordUsable = true;
vm.choseGroupList = [];
for (var key in vm.groupList){
vm.groupList[key].chose = true;
}
}
vm.groupList[vm.group]. chose = true;
vm.choseGroupList.push({name:vm.groupList[vm.group].name, index:vm.group, id:vm.groupList[vm.group].id});
vm.group = -1;
},
removeGroup: function(groupIndex){
if (vm.groupList[vm.choseGroupList[groupIndex].index].id == 0){
vm.passwordUsable = false;
for (key in vm.groupList){
vm.groupList[key].chose = false;
}
}
vm.groupList[vm.choseGroupList[groupIndex].index].chose = false;
vm.choseGroupList.remove(vm.choseGroupList[groupIndex]);
}
});
$.ajax({ // Get current user type
url: "/api/user/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
if (data.data.admin_type == 2) { // Is super user
vm.isGlobal = true;
vm.groupList.push({id:0,name:"所有人",chose:false});
}
$.ajax({ // Get the group list of current user
beforeSend: csrfTokenHeader,
url: "/api/admin/group/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
if (!data.data.length) {
bsAlert("您的用户权限只能创建组内比赛,但是您还没有创建过小组");
return;
}
for (var i = 0; i < data.data.length; i++) {
var item = data.data[i];
item["chose"] = false;
vm.groupList.push(item);
}
}
else {
bsAlert(data.data);
}
}
});
}
}
});
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"
});
});

View File

@ -1,231 +0,0 @@
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "datetimePicker",
"validator"],
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
avalon.vmodels.add_contest = null;
$("#add-contest-form").validator().on('submit', function (e) {
if (!e.isDefaultPrevented()){
e.preventDefault();
var ajaxData = {
title: vm.title,
description: vm.description,
mode: vm.mode,
contest_type: 0,
show_rank: vm.showRank,
show_user_submission: vm.showSubmission,
//password: vm.password,
start_time: vm.startTime,
end_time: vm.endTime,
visible: true
};
if (vm.choseGroupList[0].id == 0) //everyone | public contest
if (vm.password == "")
ajaxData.contest_type = 1;
else{
ajaxData.password = vm.password;
}
else { // Add groups info
ajaxData.groups = [];
for (var i = 0; vm.choseGroupList[i]; i++)
ajaxData.groups.push(parseInt(vm.choseGroupList[i].id))
}
console.log(ajaxData);
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/contest/",
dataType: "json",
contentType: "application/json",
data: JSON.stringify(ajaxData),
method: "post",
contentType: "application/json",
success: function (data) {
if (!data.code) {
bsAlert("添加成功!");
console.log(data);
}
else {
bsAlert(data.data);
console.log(data);
}
}
});
console.log(JSON.stringify(ajaxData));
}
return false;
});
editor("#editor");
editor("#problemDescriptionEditor");
editor("#problemHintEditor");
var vm = avalon.define({
$id: "add_contest",
title: "",
description: "",
startTime: "",
endTime: "",
password: "",
mode: "",
showRank: false,
showSubmission: false,
problems: [],
editingProblemId: 0,
editSamples: [],
editTestCaseList: [],
group: "-1",
groupList: [],
choseGroupList: [],
showProblemEditArea: function (problemIndex) {
if (vm.editingProblemId == problemIndex){
vm.problems[vm.editingProblemId-1].samples = vm.editSamples;
vm.editingProblemId = 0;
}
else {
if (vm.editingProblemId)
{
vm.problems[vm.editingProblemId-1].samples = vm.editSamples;
vm.problems[vm.editingProblemId-1].testCaseList = vm.editTestCaseList;
}
vm.editingProblemId = problemIndex;
vm.editSamples = [];
vm.editSamples = vm.problems[vm.editingProblemId-1].samples;
vm.editTestCaseList = [];
vm.editTestCaseList = vm.problems[vm.editingProblemId-1].testCaseList;
}
},
passwordUsable: false,
add_problem: function () {
var problem = {
title: "",
timeLimit: 1000,
memoryLimit: 256,
description: "",
samples: [],
visible: true,
test_case_id: "",
testCaseList: [],
hint: "",
score: 0,
uploadSuccess: false,
};
vm.problems.push(problem);
vm.showProblemEditArea(vm.problems.length);
},
del_problem: function (problemIndex) {
if (confirm("你确定要删除么?")) {
vm.editingProblemId = 0;
vm.problems.remove(vm.problems[problemIndex-1]);
}
},
hidden: function () {
vm.problems[vm.editingProblemId-1].samples = editSamples;
vm.problems[vm.editingProblemId-1].testCaseList = editTestCaseList;
vm.editingProblemId = 0;
},
toggle: function (item) {
item.visible = !item.visible;
},
add_sample: function () {
vm.editSamples.push({visible: true, input: "", output: ""});
},
del_sample: function (sample) {
if (confirm("你确定要删除么?")) {
editSamples.remove(sample);
}
},
getBtnContent: function (item) {
if (item.visible)
return "折叠";
return "展开";
},
addGroup: function() {
if (vm.group == -1) return;
if (vm.groupList[vm.group].id == 0){
vm.passwordUsable = true;
vm.choseGroupList = [];
for (var key in vm.groupList){
vm.groupList[key].chose = true;
}
}
vm.groupList[vm.group]. chose = true;
vm.choseGroupList.push({name:vm.groupList[vm.group].name, index:vm.group, id:vm.groupList[vm.group].id});
},
unchose: function(groupIndex){
if (vm.groupList[vm.choseGroupList[groupIndex].index].id == 0){
vm.passwordUsable = false;
for (key in vm.groupList){
vm.groupList[key].chose = false;
}
}
vm.groupList[vm.choseGroupList[groupIndex].index].chose = false;
vm.choseGroupList.remove(vm.choseGroupList[groupIndex]);
}
});
var isSuperAdmin = true;
$.ajax({ //用于获取该用户创建的所有小组的ajax请求
beforeSend: csrfTokenHeader,
url: "/api/admin/group/?my_group=true",
dataType: "json",
method: "get",
contentType: "application/json",
success: function (data) {
if (!data.code) {
if (isSuperAdmin)
vm.groupList.push({id:0, name:"所有人", chose: false});
for (var key in data.data) {
data.data[key].chose = false;
vm.groupList.push(data.data[key]);
}
}
else {
bsAlert(data.data);
console.log(data);
}
}
});
uploader("#uploader", "/api/admin/test_case_upload/", function (file, respond) {
if (respond.code)
bsAlert(respond.data);
else {
vm.problems[vm.editingProblemId-1].test_case_id = respond.data.test_case_id;
vm.problems[vm.editingProblemId-1].uploadSuccess = true;
vm.editTestCaseList = [];
for (var i = 0; i < respond.data.file_list.input.length; i++) {
vm.editTestCaseList.push({
input: respond.data.file_list.input[i],
output: respond.data.file_list.output[i]
});
}
vm.problems[vm.editingProblemId-1].testCaseList = vm.editTestCaseList;
bsAlert("测试数据添加成功!共添加"+vm.editTestCaseList.length +"组测试数据");
}
},
function(){
if (vm.editingProblemId == 0)
{
bsAlert("你还未指定一道题目!");
return false;
}
}
);
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"
});
});

View File

@ -0,0 +1,331 @@
require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", "validator"], function ($, avalon, csrfTokenHeader, bsAlert, editor) {
avalon.ready(function () {
$("#edit-contest-form").validator().on('submit', function (e) {
if (!e.isDefaultPrevented()){
e.preventDefault();
var ajaxData = {
id: vm.contestList[vm.editingContestId-1].id,
title: vm.editTitle,
description: vm.editDescription,
mode: vm.editMode,
contest_type: 0,
show_rank: vm.editShowRank,
show_user_submission: vm.editShowSubmission,
start_time: vm.editStartTime,
end_time: vm.editEndTime,
visible: vm.editVisible
};
if (vm.choseGroupList.length == 0) {
bsAlert("你没有选择参赛用户!");
return false;
}
if (vm.editDescription == "") {
bsAlert("比赛描述不能为空!");
return false;
}
if (vm.choseGroupList[0].id == 0) { //everyone | public contest
if (vm.editPassword) {
ajaxData.password = vm.editPassword;
ajaxData.contest_type = 2;
}
else{
ajaxData.contest_type = 1;
}
}
else { // Add groups info
ajaxData.groups = [];
for (var i = 0; vm.choseGroupList[i]; i++)
ajaxData.groups.push(parseInt(vm.choseGroupList[i].id))
}
$.ajax({ // Add contest
beforeSend: csrfTokenHeader,
url: "/api/admin/contest/",
dataType: "json",
contentType: "application/json",
data: JSON.stringify(ajaxData),
method: "put",
contentType: "application/json",
success: function (data) {
if (!data.code) {
bsAlert("修改成功!");
vm.editingContestId = 0; // Hide the editor
vm.getPage(1); // Refresh the contest list
}
else {
bsAlert(data.data);
}
}
});
}
return false;
});
if(avalon.vmodels.contestList){
// this page has been loaded before, so set the default value
var vm = avalon.vmodels.contestList;
vm.contestList= [];
vm.previousPage= 0;
vm.nextPage= 0;
vm.page= 1;
vm.totalPage= 1;
vm.group= "-1";
vm.groupList= [];
vm.choseGroupList= [];
vm.passwordUsable= false;
vm.keyword= "";
vm.editingContestId= 0;
vm.editTitle= "";
vm.editDescription= "";
vm.editProblemList= [];
vm.editPassword= "";
vm.editStartTime= "";
vm.editEndTime= "";
vm.editMode= "";
vm.editShowRank= false;
vm.editShowSubmission= false;
vm.editProblemList= [];
vm.editVisible= false;
vm.editChoseGroupList= [];
vm.editingProblemContestIndex= 0;
}
else {
var vm = avalon.define({
$id: "contestList",
contestList: [],
previousPage: 0,
nextPage: 0,
page: 1,
totalPage: 1,
showVisibleOnly: false,
group: "-1",
groupList: [],
choseGroupList: [],
passwordUsable: false,
keyword: "",
editingContestId: 0,
editTitle: "",
editDescription: "",
editProblemList: [],
editPassword: "",
editStartTime: "",
editEndTime: "",
editMode: "",
editShowRank: false,
editShowSubmission: false,
editProblemList: [],
editVisible: false,
editChoseGroupList: [],
editingProblemContestIndex: 0,
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) {
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
getPage: function (page_index) {
getPageData(page_index);
},
showEditContestArea: function (contestId) {
if (vm.editingContestId && !confirm("如果继续将丢失未保存的信息,是否继续?"))
return;
if (contestId == vm.editingContestId)
vm.editingContestId = 0;
else {
vm.editingContestId = contestId;
vm.editTitle = vm.contestList[contestId-1].title;
vm.editPassword = vm.contestList[contestId-1].password;
vm.editStartTime = vm.contestList[contestId-1].start_time.substring(0,16).replace("T"," ");
vm.editEndTime = vm.contestList[contestId-1].end_time.substring(0,16).replace("T"," ");
vm.editMode = vm.contestList[contestId-1].mode;
vm.editVisible = vm.contestList[contestId-1].visible;
if (vm.contestList[contestId-1].contest_type == 0) { //contest type == 0, contest in group
//Clear the choseGroupList
while (vm.choseGroupList.length) {
vm.removeGroup(0);
}
for (var i = 0; i < vm.contestList[contestId-1].groups.length; i++){
var id = parseInt(vm.contestList[contestId-1].groups[i]);
var index = 0;
for (; vm.groupList[index]; index++) {
if (vm.groupList[index].id == id)
break;
}
vm.groupList[index].chose = true;
vm.choseGroupList.push({
name:vm.groupList[index].name,
index:index,
id:id
});
}
}
else{
vm.group = "0";
vm.addGroup()//vm.editChoseGroupList = [0]; id 0 is for the group of everyone~
}
vm.editShowRank = vm.contestList[contestId-1].show_rank;
vm.editShowSubmission = vm.contestList[contestId-1].show_user_submission;
editor("#editor").setValue(vm.contestList[contestId-1].description);
vm.editingProblemContestIndex = 0;
}
},
showEditProblemArea: function(contestId) {
if (vm.editingProblemContestIndex == contestId) {
vm.editingProblemContestIndex = 0;
return;
}
if (vm.editingContestId&&!confirm("如果继续将丢失未保存的信息,是否继续?")){
return;
}
$.ajax({ // Get the problem list of current contest
beforeSend: csrfTokenHeader,
url: "/api/admin/contest_problem/?contest_id=" + vm.contestList[contestId-1].id,
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
vm.editProblemList = data.data;
}
else {
bsAlert(data.data);
}
}
});
vm.editingContestId = 0;
vm.editingProblemContestIndex = contestId;
vm.editMode = vm.contestList[contestId-1].mode;
},
addGroup: function() {
if (vm.group == -1) return;
if (vm.groupList[vm.group].id == 0){
vm.passwordUsable = true;
vm.choseGroupList = [];
for (var i = 0; i < vm.groupList.length; i++) {
vm.groupList[i].chose = true;
}
}
vm.groupList[vm.group]. chose = true;
// index of the group is relative. It is related to user
vm.choseGroupList.push({name:vm.groupList[vm.group].name, index:vm.group, id:vm.groupList[vm.group].id});
vm.group = -1;
},
removeGroup: function(groupIndex){
if (vm.groupList[vm.choseGroupList[groupIndex].index].id == 0){
vm.passwordUsable = false;
for (var i = 0; i < vm.groupList.length; i++) {
vm.groupList[i].chose = false;
}
}
vm.groupList[vm.choseGroupList[groupIndex].index].chose = false;
vm.choseGroupList.remove(vm.choseGroupList[groupIndex]);
},
add_problem: function () {
vm.$fire("up!showContestProblemPage", 0, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode);
},
showProblemEditor: function(el) {
vm.$fire("up!showContestProblemPage", el.id, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode);
},
getYesOrNo: function(yORn) {
if (yORn) return "是";
return "否";
}
});
vm.$watch("showVisibleOnly", function() {
getPageData(1);
})
}
getPageData(1);
//init time picker
$("#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"
});
function getPageData(page) {
var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=10";
if (vm.showVisibleOnly)
url += "&visible=true"
if (vm.keyword != "")
url += "&keyword=" + vm.keyword;
$.ajax({
url: url,
dataType: "json",
method: "get",
success: function (data) {
if (!data.code) {
vm.contestList = data.data.results;
vm.totalPage = data.data.total_page;
vm.previousPage = data.data.previous_page;
vm.nextPage = data.data.next_page;
vm.page = page;
}
else {
bsAlert(data.data);
}
}
});
}
// Get group list
$.ajax({ // Get current user type
url: "/api/user/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
if (data.data.admin_type == 2) { // Is super user
vm.isGlobal = true;
vm.groupList.push({id:0,name:"所有人",chose:false});
}
$.ajax({ // Get the group list of current user
beforeSend: csrfTokenHeader,
url: "/api/admin/group/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
if (!data.data.length) {
//this user have no group can use
return;
}
for (var i = 0; i < data.data.length; i++) {
var item = data.data[i];
item["chose"] = false;
vm.groupList.push(item);
}
}
else {
bsAlert(data.data);
}
}
});
}
}
});
});
avalon.scan();
});

View File

@ -1,139 +0,0 @@
require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker"], function ($, avalon, csrfTokenHeader, bsAlert, editor) {
avalon.ready(function () {
if (avalon.vmodels.contestList) {
vm = avalon.vmodels.contestList;
vm.editingContest = 0;
}
else {
var vm = avalon.define({
$id: "contestList",
contestList: [],
previousPage: 0,
nextPage: 0,
page: 1,
totalPage: 1,
group: "-1",
groupList: [],
keyword: "",
editingContestId: 0,
editTitle: "",
editProblemList: [],
editPassword: "",
editStartTime: "",
editEndTime: "",
editMode: "",
editShowRank: false,
editShowSubmission: false,
editProblemList: [],
editingProblemId: 0,
editSamples: [],
editTestCaseList: [],
editChoseGroupList: [],
modelNameList: ["ACM", "AC总数", "分数"],
contestTypeNameList: ["小组赛", "公开赛", "有密码保护的公开赛"],
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) {
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
getPage: function (page_index) {
getPageData(page_index);
},
showEditContestArea: function (contestId) {
if (contestId == vm.editingContestId)
vm.editingContestId = 0;
else {
vm.editingContestId = contestId;
vm.editTitle = vm.contestList[contestId - 1].title;
vm.editEndTime = vm.contestList[contestId - 1].end_time;
vm.editPassword = vm.contestList[contestId - 1].password;
vm.editStartTime = vm.contestList[contestId - 1].start_time;
vm.editMode = vm.contestList[contestId - 1].mode;
vm.editChoseGroupList = [];
//= vm.contestList[contestId-1].group;//
/*for (var key in vm.contestList[contestId-1].groups){
var id = parseInt(vm.contestList[contestId-1].groups);
for ()
vm.editChoseGroupList.push({
name:vm.groupList[vm.group].name,
index:index,
id:parseInt(vm.contestList[contestId-1].groups)
});
}*/
vm.editShowRank = vm.contestList[contestId - 1].show_rank;
vm.editShowSubmission = vm.contestList[contestId - 1].show_user_submission;
//vm.editProblemList = vm.contestList[contestId-1].problems
editor("#editor").setValue(vm.contestList[contestId - 1].description);
vm.editingProblemList = vm.contestList[contestId - 1].problemList;
}
}
});
getPageData(1);
}
function getPageData(page) {
var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=10";
if (vm.keyword != "")
url += "&keyword=" + vm.keyword;
$.ajax({
url: url,
dataType: "json",
method: "get",
success: function (data) {
if (!data.code) {
vm.contestList = data.data.results;
vm.totalPage = data.data.total_page;
vm.previousPage = data.data.previous_page;
vm.nextPage = data.data.next_page;
vm.page = page;
}
else {
bsAlert(data.data);
}
}
});
}
var isSuperAdmin = true;
$.ajax({ //用于获取该用户创建的所有小组的ajax请求
url: "/api/admin/group/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
if (!data.data.length) {
bsAlert("您的用户权限只能创建组内比赛,但是您还没有创建过小组");
return;
}
for (var i = 0; i < data.data.length; i++) {
var item = data.data[i];
item["chose"] = false;
vm.groupList.push(item);
}
}
else {
bsAlert(data.data);
}
}
});
});
avalon.scan();
});

View File

@ -0,0 +1,203 @@
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI"],
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
avalon.ready(function () {
$("#edit-problem-form").validator()
.on('submit', function (e) {
if (!e.isDefaultPrevented()){
e.preventDefault();
if (vm.testCaseId == "") {
bsAlert("你还没有上传测试数据!");
return false;
}
if (vm.description == "") {
bsAlert("题目描述不能为空!");
return false;
}
if (vm.timeLimit < 1000 || vm.timeLimit > 5000) {
bsAlert("保证时间限制是一个1000-5000的合法整数");
return false;
}
if (vm.samples.length == 0) {
bsAlert("请至少添加一组样例!");
return false;
}
for (var i = 0; i < vm.samples.length; i++) {
if (vm.samples[i].input == "" || vm.samples[i].output == "") {
bsAlert("样例输入与样例输出不能为空!");
return false;
}
}
var ajaxData = {
title: vm.title,
description: vm.description,
time_limit: vm.timeLimit,
memory_limit: vm.memoryLimit,
samples: [],
test_case_id: vm.testCaseId,
hint: vm.hint,
visible: vm.visible,
contest_id: avalon.vmodels.admin.$contestId,
input_description: vm.inputDescription,
output_description: vm.outputDescription,
sort_index: vm.sortIndex,
};
if (vm.contestMode == '2') {
if (!vm.score) {
bsAlert("请输入有效的分值!")
return false;
}
ajaxData.score = vm.score;
}
var method = "post";
if (avalon.vmodels.admin.$problemId) {
method = "put";
ajaxData.id = avalon.vmodels.admin.$problemId;
}
for (var i = 0; i < vm.samples.$model.length; i++) {
ajaxData.samples.push({input: vm.samples.$model[i].input, output: vm.samples.$model[i].output});
}
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/contest_problem/",
dataType: "json",
data: JSON.stringify(ajaxData),
method: method,
contentType: "application/json",
success: function (data) {
if (!data.code) {
bsAlert("题目编辑成功!");
vm.goBack(true);
}
else {
bsAlert(data.data);
}
}
});
return false;
}
});
if (!avalon.vmodels.editProblem)
var vm = avalon.define({
$id: "editProblem",
title: "",
description: "",
timeLimit: 0,
memoryLimit: 0,
samples: [],
hint: "",
sortIndex: "",
visible: true,
inputDescription: "",
outputDescription: "",
testCaseIdd: "",
contestMode: 0,
score: 1,
uploadSuccess: false,
testCaseList: [],
addSample: function () {
vm.samples.push({input: "", output: "", "visible": true});
},
delSample: function (sample) {
if (confirm("你确定要删除么?")) {
vm.samples.remove(sample);
}
},
toggleSample: function (sample) {
sample.visible = !sample.visible;
},
getBtnContent: function (item) {
if (item.visible)
return "折叠";
return "展开";
},
goBack: function(check){
if (check||confirm("这将丢失所有的改动,确定要继续么?")) {
vm.$fire("up!showContestListPage");
}
}
});
else
vm = avalon.vmodels.editProblem;
var hintEditor = editor("#hint");
var descriptionEditor = editor("#problemDescription");
var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) {
if (response.code)
bsAlert(response.data);
else {
vm.testCaseId = response.data.test_case_id;
vm.uploadSuccess = true;
vm.testCaseList = [];
for (var i = 0; i < response.data.file_list.input.length; i++) {
vm.testCaseList.push({
input: response.data.file_list.input[i],
output: response.data.file_list.output[i]
});
}
bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据");
}
});
vm.contestMode = avalon.vmodels.admin.$contestMode;
if (avalon.vmodels.admin.$problemId){
$.ajax({
url: "/api/admin/contest_problem/?contest_problem_id=" + avalon.vmodels.admin.$problemId,
method: "get",
dataType: "json",
success: function (data) {
if (data.code) {
bsAlert(data.data);
}
else { // Edit mode load the problem data
var problem = data.data;
vm.testCaseList = [];
vm.sortIndex = problem.sort_index;
vm.title = problem.title;
vm.description = problem.description;
vm.timeLimit = problem.time_limit;
vm.memoryLimit = problem.memory_limit;
vm.hint = problem.hint;
vm.visible = problem.visible;
vm.inputDescription = problem.input_description;
vm.outputDescription = problem.output_description;
vm.score = problem.score;
vm.samples = [];
vm.testCaseId = problem.test_case_id;
for (var i = 0; i < problem.samples.length; i++) {
vm.samples.push({
input: problem.samples[i].input,
output: problem.samples[i].output,
visible: false
})
}
hintEditor.setValue(vm.hint);
descriptionEditor.setValue(vm.description);
}
}
});
}
else { //Create new problem Set default values
vm.testCaseList = [];
vm.title = "";
vm.timeLimit = 1000;
vm.memoryLimit = 256;
vm.samples = [];
vm.visible = true;
vm.inputDescription = "";
vm.outputDescription = "";
vm.testCaseId = "";
vm.sortIndex = "";
vm.score = 0;
hintEditor.setValue("");
descriptionEditor.setValue("");
}
});
avalon.scan();
});

View File

@ -1,7 +1,7 @@
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI"], require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI"],
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
avalon.ready(function () { avalon.ready(function () {
avalon.vmodels.addProblem = null;
$("#add-problem-form").validator() $("#add-problem-form").validator()
.on('submit', function (e) { .on('submit', function (e) {
if (!e.isDefaultPrevented()){ if (!e.isDefaultPrevented()){
@ -63,6 +63,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
bsAlert("题目添加成功!"); bsAlert("题目添加成功!");
location.hash = "problem/problem_list";
} }
else { else {
bsAlert(data.data); bsAlert(data.data);
@ -92,41 +93,61 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
var hintEditor = editor("#hint"); var hintEditor = editor("#hint");
var problemDescription = editor("#problemDescription"); var problemDescription = editor("#problemDescription");
if (avalon.vmodels.addProblem) {
var vm = avalon.define({ var vm = avalon.vmodels.addProblem;
$id: "addProblem", vm.title = "";
title: "", vm.description = "";
description: "", vm.timeLimit = 1000;
timeLimit: 1000, vm.memoryLimit = 256;
memoryLimit: 256, vm.samples = [{input: "", output: "", "visible": true}];
samples: [{input: "", output: "", "visible": true}], vm.hint = "";
hint: "", vm.visible = true;
visible: true, vm.difficulty = 0;
difficulty: 0, vm.tags = [];
tags: [], vm.inputDescription = "";
inputDescription: "", vm.outputDescription = "";
outputDescription: "", vm.testCaseId = "";
testCaseId: "", vm.testCaseList = [];
testCaseList: [], vm.uploadSuccess = false;
uploadSuccess: false, vm.source = "";
source: "", hintEditor.setValue("");
addSample: function () { problemDescription.setValue("");
vm.samples.push({input: "", output: "", "visible": true}); }
}, else
delSample: function (sample) { var vm = avalon.define({
if (confirm("你确定要删除么?")) { $id: "addProblem",
vm.samples.remove(sample); title: "",
description: "",
timeLimit: 1000,
memoryLimit: 256,
samples: [{input: "", output: "", "visible": true}],
hint: "",
visible: true,
difficulty: 0,
tags: [],
inputDescription: "",
outputDescription: "",
testCaseId: "",
testCaseList: [],
uploadSuccess: false,
source: "",
addSample: function () {
vm.samples.push({input: "", output: "", "visible": true});
},
delSample: function (sample) {
if (confirm("你确定要删除么?")) {
vm.samples.remove(sample);
}
},
toggleSample: function (sample) {
sample.visible = !sample.visible;
},
getBtnContent: function (item) {
if (item.visible)
return "折叠";
return "展开";
} }
}, });
toggleSample: function (sample) {
sample.visible = !sample.visible;
},
getBtnContent: function (item) {
if (item.visible)
return "折叠";
return "展开";
}
});
var tagAutoCompleteList = []; var tagAutoCompleteList = [];

View File

@ -3,8 +3,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
avalon.ready(function () { avalon.ready(function () {
$("#edit-problem-form").validator() $("#edit-problem-form").validator()
.on('submit', function (e) { .on('submit', function (e) {
if (!e.isDefaultPrevented()){ if (!e.isDefaultPrevented()){
@ -66,6 +64,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
bsAlert("题目编辑成功!"); bsAlert("题目编辑成功!");
vm.showProblemListPage();
} }
else { else {
bsAlert(data.data); bsAlert(data.data);
@ -76,49 +75,60 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
return false; return false;
} }
}); });
if (avalon.vmodels.editProblem) {
if(avalon.vmodels.editProblem){ var vm = avalon.vmodels.editProblem;
var vm = avalon.vmodels.editProblem; title: "",
} description= "";
else { timeLimit= -1;
var vm = avalon.define({ memoryLimit= -1;
$id: "editProblem", samples= [];
title: "", hint= "";
description: "", visible= true;
timeLimit: -1, difficulty= 0;
memoryLimit: -1, inputDescription= "";
samples: [], outputDescription= "";
hint: "", testCaseIdd= "";
visible: true, uploadSuccess= false;
difficulty: 0, source= "";
inputDescription: "", testCaseList= [];
outputDescription: "", }
testCaseIdd: "", else
uploadSuccess: false, var vm = avalon.define({
source: "", $id: "editProblem",
testCaseList: [], title: "",
addSample: function () { description: "",
vm.samples.push({input: "", output: "", "visible": true}); timeLimit: -1,
}, memoryLimit: -1,
delSample: function (sample) { samples: [],
if (confirm("你确定要删除么?")) { hint: "",
vm.samples.remove(sample); visible: true,
} difficulty: 0,
}, inputDescription: "",
toggleSample: function (sample) { outputDescription: "",
sample.visible = !sample.visible; testCaseIdd: "",
}, uploadSuccess: false,
getBtnContent: function (item) { source: "",
if (item.visible) testCaseList: [],
return "折叠"; addSample: function () {
return "展开"; vm.samples.push({input: "", output: "", "visible": true});
}, },
showProblemListPage: function () { delSample: function (sample) {
vm.$fire("up!showProblemListPage"); if (confirm("你确定要删除么?")) {
vm.samples.remove(sample);
} }
}); },
toggleSample: function (sample) {
} sample.visible = !sample.visible;
},
getBtnContent: function (item) {
if (item.visible)
return "折叠";
return "展开";
},
showProblemListPage: function(){
vm.$fire("up!showProblemListPage");
}
});
var hintEditor = editor("#hint"); var hintEditor = editor("#hint");
var descriptionEditor = editor("#problemDescription"); var descriptionEditor = editor("#problemDescription");
var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) { var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) {
@ -148,7 +158,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
} }
else { else {
var problem = data.data; var problem = data.data;
console.log(problem);
vm.title = problem.title; vm.title = problem.title;
vm.description = problem.description; vm.description = problem.description;
vm.timeLimit = problem.time_limit; vm.timeLimit = problem.time_limit;
@ -204,4 +213,4 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
}); });
avalon.scan(); avalon.scan();
}); });

View File

@ -41,10 +41,8 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
vm.$fire("up!showProblemSubmissionPage", problemId); vm.$fire("up!showProblemSubmissionPage", problemId);
} }
}); });
getPageData(1);
} }
getPageData(1);
function getPageData(page) { function getPageData(page) {
var url = "/api/admin/problem/?paging=true&page=" + page + "&page_size=10"; var url = "/api/admin/problem/?paging=true&page=" + page + "&page_size=10";
if (vm.keyword != "") if (vm.keyword != "")

View File

@ -40,6 +40,12 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
}, },
getPage: function (page_index) { getPage: function (page_index) {
getPageData(page_index); getPageData(page_index);
},
showSubmissionDetailPage: function (submissionId) {
},
showProblemListPage: function(){
vm.$fire("up!showProblemListPage");
} }
}); });
@ -66,9 +72,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
}); });
} }
avalon.scan();
}); });
avalon.scan();
}); });

View File

@ -3,58 +3,68 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
// avalon:定义模式 userList // avalon:定义模式 userList
avalon.ready(function () { avalon.ready(function () {
avalon.vmodels.userList = null; //avalon.vmodels.userList = null;
var vm = avalon.define({ if (avalon.vmodels.userList) {
$id: "userList", var vm = avalon.vmodels.userList;
//通用变量 // initialize avalon object
userList: [], userList = []; previousPage= 0; nextPage= 0; page = 1;
previousPage: 0, editingUserId= 0; totalPage = 1; keyword= ""; showAdminOnly= false;
nextPage: 0, //user editor fields
page: 1, username= ""; realName= ""; email= ""; adminType= 0; id= 0;
editingUserId: 0, }
totalPage: 1, else {
userType: ["一般用户", "管理员", "超级管理员"], var vm = avalon.define({
keyword: "", $id: "userList",
showAdminOnly: false, //通用变量
//编辑区域同步变量 userList: [],
username: "", previousPage: 0,
realName: "", nextPage: 0,
email: "", page: 1,
adminType: 0, editingUserId: 0,
id: 0, totalPage: 1,
getNext: function () { userType: ["一般用户", "管理员", "超级管理员"],
if (!vm.nextPage) keyword: "",
return; showAdminOnly: false,
getPageData(vm.page + 1); //编辑区域同步变量
}, username: "",
getPrevious: function () { realName: "",
if (!vm.previousPage) email: "",
return; adminType: 0,
getPageData(vm.page - 1); id: 0,
}, getNext: function () {
getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑 if (!vm.nextPage)
if (btn) { return;
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
editUser: function (user) { //点击编辑按钮的事件,显示/隐藏编辑区
vm.username = user.username;
vm.realName = user.real_name;
vm.adminType = user.admin_type;
vm.email = user.email;
vm.id = user.id;
if (vm.editingUserId == user.id)
vm.editingUserId = 0;
else
vm.editingUserId = user.id;
},
search: function () {
getPageData(1);
} }
else { });
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; }
}
},
editUser: function (user) { //点击编辑按钮的事件,显示/隐藏编辑区
vm.username = user.username;
vm.realName = user.real_name;
vm.adminType = user.admin_type;
vm.email = user.email;
vm.id = user.id;
if (vm.editingUserId == user.id)
vm.editingUserId = 0;
else
vm.editingUserId = user.id;
},
search: function () {
getPageData(1);
}
});
vm.$watch("showAdminOnly", function () { vm.$watch("showAdminOnly", function () {
getPageData(1); getPageData(1);
}); });

View File

@ -156,5 +156,4 @@ 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})

View File

@ -6,8 +6,9 @@
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<div class="form-group"> <div class="form-group">
<input type="text" name="name" class="form-control" ms-duplex="title" <input type="text" name="name" class="form-control" ms-duplex="title"
data-error="请填写比赛名称(名称不能超过50个字)" required> data-error="请填写比赛名称(名称不能超过50个字)" ms-attr-readonly="contestCreated" required>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
</div> </div>
@ -17,51 +18,46 @@
<div class="col-md-12"> <div class="col-md-12">
<div class="form-group"> <div class="form-group">
<textarea id="editor" placeholder="这里输入内容" autofocus ms-duplex="description"></textarea> <textarea id="editor" placeholder="这里输入内容" autofocus ms-duplex="description"></textarea>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
<small ms-visible="description==''" style="color:red">请填写比赛描述</small> <small ms-visible="description==''" style="color:red">请填写比赛描述</small>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label>开始时间</label> <label>开始时间</label>
</div>
<div class="col-md-6">
<label>结束时间</label>
</div>
<div class="col-md-6">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control" name="start_time" id="contest_start_time" <input type="text" class="form-control" name="start_time" id="contest_start_time"
ms-duplex="startTime" data-error="请填写比赛开始时间" required> ms-duplex="startTime" data-error="请填写比赛开始时间" required>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label>结束时间</label>
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control" name="end_time" id="contest_end_time" <input type="text" class="form-control" name="end_time" id="contest_end_time"
ms-duplex="endTime" data-error="请填写比赛结束时间" required> ms-duplex="endTime" data-error="请填写比赛结束时间" required>
<div class="help-block with-errors"></div>
</div> <div class="help-block with-errors"></div>
</div>
<div class="col-md-6">
<label>允许参加的用户</label>
</div>
<div class="col-md-6" >
<label>密码保护</label>
</div>
<div class="col-md-6">
<div class="form-group">
<select class="form-control" name="password" ms-duplex="group" ms-change="addGroup" value="-1">
<option value="-1">请选择</option>
<option ms-repeat="groupList" ms-attr-value="$index" ms-visible="!el.chose">{{el.name}}</option>
</select>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label>允许参加的用户</label>
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password" ms-attr-readonly="!passwordUsable"> <select class="form-control" ms-duplex="group" ms-change="addGroup" value="-1">
<option value="-1">请选择</option>
<option ms-repeat="groupList" ms-attr-value="$index" ms-visible="!el.chose">{{el.name}}</option>
</select>
</div>
</div>
<div class="col-md-6" ms-visible="passwordUsable">
<label>密码保护</label>
<div class="form-group">
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password">
</div> </div>
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<div ms-repeat="choseGroupList" class="group-tag" ms-click="unchose($index)">{{el.name}}</div> <div ms-repeat="choseGroupList" class="group-tag" ms-click="removeGroup($index)">{{el.name}}</div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label>排名方式</label> <label>排名方式</label>
@ -94,135 +90,12 @@
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="form-group"> <div class="form-group">
<label class="text"><input type="checkbox" ms-duplex-checked="showSbumission"> <label class="text"><input type="checkbox" ms-duplex-checked="showSubmission">
<small>允许查看提交记录</small> <small>允许查看提交记录</small>
</label> </label>
</div> </div>
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<label>添加题目</label>
<a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="add_problem()">添加</a>
<table class="table table-striped">
<tr>
<th>编号</th>
<th>题目</th>
<th>测试数据</th>
<td></td>
</tr>
<tr ms-repeat="problems">
<td>题目{{ $index+1 }}</td>
<td>{{ el.title }}</td>
<td>{{ el.testCaseList.length }}组</td>
<td>
<a href="javascript:void(0)"class="btn-sm btn-info" ms-click="showProblemEditArea($index+1)">编辑</a>
<a href="javascript:void(0)"class="btn-sm btn-danger" ms-click="del_problem($index+1)">删除</a>
</td>
</tr>
</table>
</div>
<div class="col-md-12">
<div class="problem" ms-visible="editingProblemId">
<div class="panel panel-default problem-panel">
<div class="panel-heading">
<span class="panel-title">题目{{editingProblemId}} </span>
<a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="hidden()">隐藏</a>
<a href="javascript:void(0)" class="btn btn-danger btn-sm" ms-click="del_problem(editingProblemId)">删除</a>
</div>
<div class="panel-body" >
<div class="form-group col-md-12">
<label>题目标题</label>
<input type="text" name="problemName" class="form-control" ms-duplex="problems[editingProblemId-1].title"data-error="请填写题目标题" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group col-md-12">
<label>题目描述</label>
<textarea id="problemDescriptionEditor" placeholder="这里输入内容"
ms-duplex="problems[editingProblemId-1].description"></textarea>
<small ms-visible="editDescription==''" style="color:red">请填写题目描述</small>
</div>
<div class="form-group col-md-12">
<label>提示</label>
<textarea id="problemHintEditor" placeholder="这里输入内容"
ms-duplex="problems[editingProblemId-1].hint"></textarea>
</div>
<div class="col-md-3 form-group">
<label>cpu</label>
<input type="number" class="form-control" ms-duplex="problems[editingProblemId-1].timeLimit" data-error="请填写时间限制" required>
<div class="help-block with-errors"></div>
</div>
<div class="col-md-3 form-group">
<label>内存</label>
<input type="number" class="form-control" ms-duplex="problems[editingProblemId-1].memoryLimit" data-error="请填写内存限制" required>
<div class="help-block with-errors"></div>
</div>
<div class="col-md-3 form-group" ms-visible="mode==2">
<label>分值</label>
<input type="number" class="form-control" ms-duplex="problems[editingProblemId-1].score" data-error="请填写题目分值" required>
<div class="help-block with-errors"></div>
</div>
<div class="col-md-12">
<label>样例</label>
<a href="javascript:void(0)" class="btn btn-primary btn-sm"
ms-click="add_sample()">添加</a>
<div>
<div class="panel panel-default sample-panel" ms-repeat-sample="editSamples">
<div class="panel-heading">
<span class="panel-title">样例{{$index + 1}}</span>
<a href="javascript:void(0)" class="btn btn-primary btn-sm"
ms-click="toggle(sample)">{{ getBtnContent(sample)}}</a>
<a href="javascript:void(0)" class="btn btn-danger btn-sm"
ms-click="del_sample(sample)">删除</a>
</div>
<div class="panel-body row" ms-visible="sample.visible">
<div class="col-md-6">
<div class="form-group">
<label>样例输入</label>
<textarea class="form-control" rows="5"
ms-duplex="sample.input"></textarea>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>样例输出</label>
<textarea class="form-control" rows="5"
ms-duplex="sample.output"></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-12">
<label>测试数据</label>
<table class="table table-striped">
<tr>
<td>编号</td>
<td>输入文件名</td>
<td>输出文件名</td>
</tr>
<tr ms-repeat="editTestCaseList">
<td>{{$index}}</td>
<td>{{ el.input }}</td>
<td>{{ el.output }}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-12">
<label>上传测试测试数据</label>
<div id="uploader">
<div>选择文件</div>
</div>
<small class="text-info">请将所有测试用例打包在一个文件中上传所有文件要在压缩包的根目录且输入输出文件名要以从1开始连续数字标识要对应例如<br>
1.in 1.out 2.in 2.out
</small><br>
<input type="submit" class="btn btn-success btn-lg" value="发布比赛"> <input type="submit" class="btn btn-success btn-lg" value="发布比赛">
</div> </div>
</div> </div>
@ -230,4 +103,4 @@
</form> </form>
</div> </div>
<script src="/static/js/app/admin/contest/add_contest.js"></script> <script src="/static/js/app/admin/contest/addContest.js"></script>

View File

@ -15,154 +15,174 @@
<tr> <tr>
<th>ID</th> <th>ID</th>
<th>比赛</th> <th>比赛</th>
<th>比赛类型</th>
<th>公开排名</th> <th>公开排名</th>
<th>开始时间</th> <th>可见</th>
<th>结束时间</th>
<th>创建时间</th> <th>创建时间</th>
<th>创建者</th> <th>创建者</th>
<th></th>
</tr> </tr>
<tr ms-repeat="contestList" ms-click="showEditContestArea($index+1)"> <tr ms-repeat="contestList">
<td>{{ el.id }}</td> <td>{{ el.id }}</td>
<td>{{ el.title }}</td> <td>{{ el.title }}</td>
<td>{{ contestTypeNameList[el.contest_type] }}</td> <td>{{ getYesOrNo(el.show_rank) }}</td>
<td>{{ el.show_rank }}</td> <td>{{ getYesOrNo(el.visible) }}</td>
<td>{{ el.start_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
<td>{{ el.end_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
<td>{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}</td> <td>{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
<td>{{ el.created_by.username }}</td> <td>{{ el.created_by.username }}</td>
<td>
<a class="btn btn-info" href="javascript:void(0)" ms-click="showEditContestArea($index+1)">编辑</a>
<a class="btn btn-primary" href="javascript:void(0)" ms-click="showEditProblemArea($index+1)">编辑问题</a>
</td>
</tr> </tr>
</table> </table>
<div class="form-group">
<label>仅显示可见 <input ms-duplex-checked="showVisibleOnly" type="checkbox"/></label>
</div>
<div class="text-right"> <div class="text-right">
页数:{{ page }}/{{ totalPage }}&nbsp;&nbsp; 页数:{{ page }}/{{ totalPage }}&nbsp;&nbsp;
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button> <button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button> <button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
</div> </div>
<div ms-visible="editingContestId"> <div ms-visible="editingContestId">
<div class="col-md-12"> <form id="edit-contest-form">
<label>比赛名称</label> <div class="col-md-12">
</div> <label>比赛名称</label>
<div class="col-md-12"> </div>
<div class="form-group"> <div class="col-md-12">
<input type="text" name="name" class="form-control" ms-duplex="editTitle" <div class="form-group">
data-error="请填写比赛名称(名称不能超过50个字)" required> <input type="text" name="name" class="form-control" ms-duplex="editTitle"
data-error="请填写比赛名称(名称不能超过50个字)" required>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div>
</div> </div>
</div> <div class="col-md-12">
<div class="col-md-12"> <label>说明</label>
<label>说明</label>
</div>
<div class="col-md-12">
<div class="form-group">
<textarea id="editor" placeholder="这里输入内容" autofocus ms-duplex="editDescription"></textarea>
<div class="help-block with-errors"></div>
<small ms-visible="description==''" style="color:red">请填写比赛描述</small>
</div> </div>
</div> <div class="col-md-12">
<div class="col-md-6"> <div class="form-group">
<label>开始时间</label> <textarea id="editor" placeholder="这里输入内容" autofocus ms-duplex="editDescription"></textarea>
</div>
<div class="col-md-6"> <div class="help-block with-errors"></div>
<label>结束时间</label> <small ms-visible="editDescription==''" style="color:red">请填写比赛描述</small>
</div> </div>
<div class="col-md-6"> </div>
<div class="form-group"> <div class="col-md-6">
<input type="text" class="form-control" name="start_time" id="contest_start_time" <label>开始时间</label>
ms-duplex="editStartTime" data-error="请填写比赛开始时间" required>
<div class="help-block with-errors"></div> <div class="form-group">
</div> <input type="text" class="form-control" name="start_time" id="contest_start_time"
</div> ms-duplex="editStartTime" data-error="请填写比赛开始时间" required>
<div class="col-md-6">
<div class="form-group"> <div class="help-block with-errors"></div>
<input type="text" class="form-control" name="end_time" id="contest_end_time" </div>
ms-duplex="editEndTime" data-error="请填写比赛结束时间" required> </div>
<div class="help-block with-errors"></div> <div class="col-md-6">
</div> <label>结束时间</label>
</div>
<div class="col-md-6"> <div class="form-group">
<label>允许参加的用户</label> <input type="text" class="form-control" name="end_time" id="contest_end_time"
</div> ms-duplex="editEndTime" data-error="请填写比赛结束时间" required>
<div class="col-md-6">
<label>密码保护</label> <div class="help-block with-errors"></div>
</div> </div>
<div class="col-md-6"> </div>
<div class="form-group"> <div class="col-md-6">
<select class="form-control" name="password" ms-duplex="group" ms-change="addGroup" value="-1"> <label>允许参加的用户</label>
<option value="-1">请选择</option>
<option ms-repeat="groupList" ms-attr-value="$index" ms-visible="!el.chose">{{el.name}}</option> <div class="form-group">
</select> <select class="form-control" ms-duplex="group" ms-change="addGroup" value="-1">
</div> <option value="-1">请选择</option>
</div> <option ms-repeat="groupList" ms-attr-value="$index" ms-visible="!el.chose">{{el.name}}</option>
<div class="col-md-6"> </select>
<div class="form-group"> </div>
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="editPassword" </div>
ms-attr-readonly="!passwordUsable"> <div class="col-md-6" ms-visible="passwordUsable">
</div> <label>密码保护</label>
</div> <input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="editPassword">
<div class="col-md-12"> </div>
<div ms-repeat="choseGroupList" class="group-tag" ms-click="unchose($index)">{{el.name}}</div> <div class="col-md-12">
</div> <div ms-repeat="choseGroupList" class="group-tag" ms-click="removeGroup($index)">{{el.name}}</div>
<div class="col-md-6"> </div>
<label>排名方式</label> <div class="col-md-6">
</div>
<div class="col-md-3"> </div>
<label>结束前是否开放排名</label> <div class="col-md-3">
</div>
<div class="col-md-3"> </div>
<label>是否公开提交记录</label> <div class="col-md-3">
</div>
<div class="col-md-6"> </div>
<div class="form-group"> <div class="col-md-4">
<label><input type="radio" name="mode" ms-duplex-string="editMode" value="0"> <label>排名方式</label>
<small>ACM</small>
</label> <div class="form-group">
<label><input type="radio" name="mode" ms-duplex-string="editMode" value="1"> <label><input type="radio" name="mode" ms-duplex-string="editMode" value="0">
<small>AC数量</small> <small>ACM</small>
</label> </label>
<label><input type="radio" name="mode" ms-duplex-string="editMode" value="2"> <label><input type="radio" name="mode" ms-duplex-string="editMode" value="1">
<small>分数</small> <small>AC数量</small>
</label> </label>
</div> <label><input type="radio" name="mode" ms-duplex-string="editMode" value="2">
</div> <small>分数</small>
<div class="col-md-3"> </label>
<div class="form-group"> </div>
<label class="text"><input type="checkbox" ms-duplex-checked="editShowRank"> </div>
<small>开放排名</small> <div class="col-md-2">
</label> <label>是否可见</label><br>
</div> <label><input type="checkbox" ms-duplex-checked="editVisible">
</div> <small> 可见</small>
<div class="col-md-3"> </label>
<div class="form-group"> </div>
<label class="text"><input type="checkbox" ms-duplex-checked="editShowSubmission"> <div class="col-md-3">
<small>允许查看提交记录</small> <label>结束前是否开放排名</label>
</label>
</div> <div class="form-group">
</div> <label class="text"><input type="checkbox" ms-duplex-checked="editShowRank">
<div class="col-md-12"> <small>开放排名</small>
<label>添加题目</label> </label>
<a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="add_problem()">添加</a> </div>
<table class="table table-striped"> </div>
<tr> <div class="col-md-3">
<th>编号</th> <label>是否公开提交记录</label>
<th>题目</th>
<th>测试数据</th> <div class="form-group">
<td></td> <label class="text"><input type="checkbox" ms-duplex-checked="editShowSubmission">
</tr> <small>允许查看提交记录</small>
<tr ms-repeat="editProblemList"> </label>
<td>题目{{ $index+1 }}</td> </div>
<td>{{ el.title }}</td> </div>
<td>{{ el.testCaseList.length }}组</td> <div class="col-md-12">
<td> <button class="btn btn-primary" type="submit">保存修改</button>
<a href="javascript:void(0)" class="btn-sm btn-info" </div>
ms-click="showProblemEditArea($index+1)">编辑</a> </form>
<a href="javascript:void(0)" class="btn-sm btn-danger" ms-click="del_problem($index+1)">删除</a> </div>
</td> <div class="col-md-12" ms-visible="editingProblemContestIndex">
</tr> <label>题目列表</label>
</table> <a href="javascript:void(0)" class="btn btn-success btn-sm" ms-click="add_problem()">添加</a>
<table class="table table-striped">
<tr>
<th>编号</th>
<th>题目</th>
<th ms-visible="editMode=='2'">分值</th>
<th>可见</th>
<th>创建时间</th>
<td></td>
</tr>
<tr ms-repeat="editProblemList">
<td>{{ el.sort_index }}</td>
<td>{{ el.title }}</td>
<td ms-visible="editMode=='2'">{{ el.score}}</td>
<td>{{ getYesOrNo(el.visible) }}</td>
<td>{{ el.create_time|date("yyyy-MM-dd HH:mm:ss") }}</td>
<td>
<a href="javascript:void(0)" class="btn-sm btn-info"
ms-click="showProblemEditor(el)">编辑</a>
</td>
</tr>
</table>
</div>
</div> </div>
</div> </div>
<script src="/static/js/app/admin/contest/contest_list.js"></script> <script src="/static/js/app/admin/contest/contestList.js"></script>

View File

@ -0,0 +1,138 @@
<div ms-controller="editProblem" class="col-md-9">
<form id="edit-problem-form">
<nav>
<ul class="pager">
<li class="previous" ms-click="goBack(0)"><a href="javascript:void(0)"><span
aria-hidden="true">&larr;</span> 返回</a></li>
</ul>
</nav>
<div class="form-group col-md-3">
<label>题目编号</label>
<input type="text" name="title" autofocus class="form-control" ms-duplex="sortIndex"
data-error="请填写题目编号" maxlength="30" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group col-md-9">
<label>题目标题</label>
<input type="text" name="title" autofocus class="form-control" ms-duplex="title"
data-error="请填写题目名称" maxlength="30" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group col-md-12">
<label>题目描述</label>
<textarea id="problemDescription" placeholder="这里输入内容(此内容不能为空)" ms-duplex="description"></textarea>
<small ms-visible="description==''" style="color:red">请填写题目描述</small>
</div>
<div class="col-md-3">
<div class="form-group"><label>时间限制(ms)</label>
<input type="number" name="timeLimit" class="form-control" ms-duplex="timeLimit"
data-error="请输入时间限制(保证是一个1000-5000的合法整数)" required>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-3">
<div class="form-group"><label>内存限制(MB)</label>
<input type="number" name="memory" class="form-control" ms-duplex="memoryLimit"
data-error="请输入内存限制(保证是一个合法整数)" required>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-3" ms-visible="contestMode=='2'">
<div class="form-group"><label>分值(仅计分模式)</label>
<input type="number" name="score" class="form-control" ms-duplex="score">
</div>
</div>
<div class="col-md-3 form-group">
<label>是否可见</label><br>
<label><input type="checkbox" ms-duplex-checked="visible">
<small> 可见</small>
</label>
</div>
<div class="col-md-12 form-group">
<label>输入描述</label><br>
<textarea class="form-control" rows="5" name="input_description"
ms-duplex="inputDescription" data-error="请填写输入描述"
maxlength="10000" required>
</textarea>
<div class="help-block with-errors"></div>
</div>
<div class="col-md-12 form-group">
<label>输出描述</label><br>
<textarea class="form-control" rows="5" name="output_escription"
ms-duplex="outputDescription" data-error="请填写输出描述"
maxlength="10000" required>
</textarea>
<div class="help-block with-errors"></div>
</div>
<div class="col-md-12"><br>
<label>样例</label>
<a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="addSample()">添加</a>
<div class="sample">
<div class="panel panel-default sample-panel" ms-repeat-sample="samples">
<div class="panel-heading">
<span class="panel-title">样例{{$index + 1}}</span>
<a href="javascript:void(0)" class="btn btn-primary btn-sm"
ms-click="toggleSample(sample)">
{{ getBtnContent(sample)}}
</a>
<a href="javascript:void(0)" class="btn btn-danger btn-sm"
ms-click="delSample(sample)">
删除
</a>
</div>
<div class="panel-body row" ms-visible="sample.visible">
<div class="col-md-6">
<div class="form-group">
<label>样例输入</label>
<textarea class="form-control" rows="5" ms-duplex="sample.input"></textarea>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>样例输出</label>
<textarea class="form-control" rows="5" ms-duplex="sample.output"></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-12"><br>
<label>测试数据(多次上传将覆盖原有测试用例)</label><br>
<small class="text-info">请将所有测试用例打包在一个文件中上传所有文件要在压缩包的根目录且输入输出文件名要以从1开始连续数字标识要对应例如<br>
1.in 1.out 2.in 2.out
</small>
<table class="table table-striped" ms-visible="uploadSuccess">
<tr>
<td>编号</td>
<td>输入文件名</td>
<td>输出文件名</td>
</tr>
<tr ms-repeat="testCaseList">
<td>{{ $index + 1 }}</td>
<td>{{ el.input }}</td>
<td>{{ el.output }}</td>
</tr>
</table>
</div>
<div class="col-md-12">
<div class="form-group">
<div id="testCaseFile">选择文件</div>
</div>
</div>
<div class="form-group col-md-12">
<label>提示</label>
<textarea id="hint" placeholder="这里输入内容" ms-duplex="hint"></textarea>
</div>
<div class="col-md-12">
<input type="submit" class="btn btn-success btn-lg" value="发布题目" id="submitBtn">
</div>
</form>
</div>
<script src="/static/js/app/admin/contest/editProblem.js"></script>

View File

@ -134,4 +134,4 @@
</form> </form>
</div> </div>
<script src="/static/js/app/admin/problem/add_problem.js"></script> <script src="/static/js/app/admin/problem/addProblem.js"></script>

View File

@ -140,4 +140,4 @@
</form> </form>
</div> </div>
<script src="/static/js/app/admin/problem/edit_problem.js"></script> <script src="/static/js/app/admin/problem/editProblem.js"></script>

View File

@ -1,4 +1,10 @@
<div ms-controller="submissionList" class="col-md-9"> <div ms-controller="submissionList" class="col-md-9">
<nav>
<ul class="pager">
<li class="previous" ms-click="showProblemListPage()"><a href="javascript:void(0)"><span
aria-hidden="true">&larr;</span> 返回</a></li>
</ul>
</nav>
<h1>提交列表</h1> <h1>提交列表</h1>
<table class="table table-striped"> <table class="table table-striped">
@ -25,4 +31,4 @@
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button> <button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
</div> </div>
</div> </div>
<script src="/static/js/app/admin/problem/submission_list.js"></script> <script src="/static/js/app/admin/problem/submissionList.js"></script>

View File

@ -0,0 +1,30 @@
{% extends "oj_base.html" %}
{% block body %}
{% load contest %}
<div class="container main">
<div class="col-lg-12 contest-tab">
<ul class="nav nav-tabs nav-tabs-google">
<li role="presentation" class="active">
<a href="/contest/{{ contest.id }}/">比赛详情</a>
</li>
<li role="presentation">
<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="row">
<div class="col-lg-12">
</div>
</div>
</div>
{% endblock %}
{% block js_block %}
{% endblock %}