mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2024-09-21 00:13:18 +00:00
Merge branch 'dev' into virusdefender-dev
* dev: ¢ûÂ䆉∫ÜÊØî˵õÂàóË°®È°µ [ÂêéÁ´Ø]ÂéªÊéâ‰∫ÜÁ∫éÁîü‰∫ßÂ∫èÂè∑ÁöÑjavascript,Êîπ‰∏∫‰ΩøÁî®Ê®°ÊùøËøáʪ§Âô®ÂÆûÁé∞(ÊàëÁöÑÊâÄÊúâÊèê‰∫§) [ÂâçÁ´Ø]‰øÆÊîπÂÆåÂñщ∫ÜÊ∑ªÂä†ÊØî˵õÈ°µÈù¢, ÊØî˵õÂàóË°®ÂäüËÉΩ‰ªç‰∏çÂÖ®Èù¢,Á®çÂêéÊîπËøõ[CI SKIP] ‰øÆÊîπ‰∫ÜcssºïÁî®Êñπºè[CI SKIP] [ÂêéÁ´Ø]‰øÆÊîπ‰∫Ücontest‰∏≠ api-docs ÁöÑÂ∞èbug[CI SKIP] Âàõª∫ÂâçÂè∞ÊØî˵õÂàóË°® ÂéªÊéâÂÜó‰ΩôËØ≠Âè•,Âõ†‰∏∫pageÂèòÈáèÂ∑≤ÁªèÊúâȪòËƧÂĺ‰∫Ü,‰∏çËÉΩ‰∏∫Á©∫ [ÂêéÁ´Ø]ÂâçÂè∞ÊàëÁöÑÊèê‰∫§È°µÈù¢ ¢ûº∫Êèê‰∫§Â∫èÂè∑ÁöÑÊòæÁ§∫,ÂéüÊù•ÊòØÊòæÁ§∫ÁúüÂÆûidÂç≥ÈöèÊú∫ÁöÑÊï£ÂàóÂĺ,‰∏ç•ΩÁúã,Áé∞Âú®ÊîπÊàêËá™ÁÑ∂Êï∞Â∫èÂàó,‰ΩÜÈúÄ˶ÅÁªìÂêàjavascriptÁîüÊàê,‰∏îÊòØÁõ∏ÂØπÂĺ,Âõ†‰∏∫Êï∞ÊçÆÂ∫ìÈáåÊ≤°ÊúâËøô‰∏™Â≠óÊƵ,ÊúâÁÇπÂà´Êâ≠‰∫Ü. Á¨¨‰∫å,Ê∑ªÂ䆉∫ÜÁî®Êà∑Ê≤°ÊúâÊèê‰∫§ËÆ∞ÂΩïÁöÑÂèçȶà. Á¨¨‰∏â,Êú¨ÊâìÁÆó¢ûÂä†Á≠õÈÄâÂäüËÉΩ,‰ΩÜÂõ†‰∏∫URLÈö扪•Áªü‰∏ĉΩúÁΩ¢,Âè™ÊúâÂú®Â¢ûÂä†Êñ∞ÁöÑurlÊâçËÉΩËæÉ•ΩÁöѧÑÁêÜ,‰∏ãʨ°ÂÜçËØ¥Êãú [ÂâçÁ´Ø]ÊØî˵õÂàóË°®È°µÈù¢(ÂêéÂè∞)ÁöÑËøõ‰∏ÄÊ≠•ÂÆåÂñÑ,‰∏çÂåÖÂê´api [ÂêéÁ´Ø]‰øÆÊîπ‰∫ÜÊàëÁöÑÊèê‰∫§ÂàóË°®ÁöÑÊ®°Êùøʆ∑ºè,Êï¥ÁêÜʆºÂºè [ÂêéÁ´Ø]‰øÆÊîπÊàëÁöÑÊèê‰∫§È°µÈù¢,ÂéªÊéâ‰∫ÜÂÜó‰ΩôËØ≠Âè•,Âπ∂Ê∑ªÂä†ÊµãËØï [ÂêéÁ´Ø-ÂâçÂè∞]Ê∑ªÂ䆉∫ÜsubmissionsÂàÜÈ°µÊòæÁ§∫(Âè™ÊòæÁ§∫ÂΩìÂâçÁî®Êà∑ÁöÑÊèê‰∫§),Ë∞ÉÁî®Â∑≤ÊúâÁöÑviewÂÆåÊàêÂçï‰∏™submissionÁöÑÊòæÁ§∫.ÊòæÁ§∫ÁïåÈù¢‰∏éÈóÆÈ¢òÂàÜÈ°µÊòæÁ§∫Áªü‰∏Ä.ÈóÆÈ¢òÊòØidÁöÑÊòæÁ§∫.url:http://127.0.0.1:8000/my_submissions/ [ÂâçÁ´Ø]‰øÆÊîπÊ∑ªÂä†ÊØî˵õÈ°µÈù¢,Êñ∞¢û‰∫܉ΩøÁî®Â∞èÁªÑapiÊü•ËØ¢ËØ•Áî®Êà∑ÊâÄÂàõª∫ÁöÑÊâÄÊúâÁöÑÂ∞èÁªÑÁöÑÂäüËÉΩ[CI SKIP] [ÂâçÁ´Ø]Ê∑ªÂä†ÊØî˵õÈ°µÈù¢Ëøõ‰∏ÄÊ≠•ÂÆåÂñÑ,Ê∑ªÂä†Â≠óÊƵÂåÖÊã¨ÊòØÂê¶ÊòæÁ§∫Êèê‰∫§,ÊØî˵õÊ®°Âºè,ÈóÆÈ¢òÂàÜÂĺ,ÂÖÅËÆ∏ÂèÇÂä†ÊØî˵õÁöÑÁî®Êà∑ÁªÑ,Âπ∂ÂÆåÂñÑÂÜÖÈÉ®ÈĪËæë,Âü∫Êú¨ÂèØÁ∫Ü,Âè™ÊòØÊ≤°ÂÜôajaxÊèê‰∫§Êï∞ÊçÆ,ÂíåÂïÊãâÂÂèñÂ∞èÁªÑ‰ø°ÊÅØÁöÑÈÉ®ÂàÜ[CI SKIP] [ÂâçÁ´Ø]‰øÆÊîπ‰∫ÜÊ∑ªÂä†ÊØî˵õÈ°µÁöÑÂΩ¢ÂºèÁªìÊûÑ,‰ªçÊúâbug[CI SKIP] Ê∑[ÂâçÁ´Ø]Ê∑ªÂä†ÊØîËÂêéÂè∞ÊØî˵õÂàóË°®[CI SKIP] [ÂâçÁ´Ø]Áªü‰∏ÄÈóÆÈ¢òÈá,ÊØî˵õÂàóË°®jsÁöÑÊñቪ∂Âêç_list.js. Ê∑ªÂ䆉∫ÜÊØî˵õÂàóË°®ÂíåÁºñËæëÊØî˵õÁöÑÈ°µÈù¢(§߉Ωì§ʆ∑ºè)[CI SKIP] Conflicts: contest/views.py
This commit is contained in:
commit
9977e156b1
@ -1,9 +1,12 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
import json
|
import json
|
||||||
|
import datetime
|
||||||
|
from django.utils.timezone import localtime
|
||||||
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
|
||||||
|
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)
|
||||||
@ -11,11 +14,13 @@ from utils.shortcuts import (serializer_invalid_response, error_response,
|
|||||||
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN
|
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN
|
||||||
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 .models import Contest, ContestProblem
|
from .models import Contest, ContestProblem
|
||||||
from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer,
|
from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer,
|
||||||
CreateContestProblemSerializer, ContestProblemSerializer,
|
CreateContestProblemSerializer, ContestProblemSerializer,
|
||||||
EditContestProblemSerializer, ContestPasswordVerifySerializer)
|
EditContestProblemSerializer, ContestPasswordVerifySerializer,
|
||||||
|
EditContestProblemSerializer)
|
||||||
|
|
||||||
|
|
||||||
class ContestAdminAPIView(APIView):
|
class ContestAdminAPIView(APIView):
|
||||||
@ -200,7 +205,7 @@ class ContestProblemAdminAPIView(APIView):
|
|||||||
"""
|
"""
|
||||||
比赛题目分页json api接口
|
比赛题目分页json api接口
|
||||||
---
|
---
|
||||||
response_serializer: ProblemSerializer
|
response_serializer: ContestProblemSerializer
|
||||||
"""
|
"""
|
||||||
contest_problem_id = request.GET.get("contest_problem_id", None)
|
contest_problem_id = request.GET.get("contest_problem_id", None)
|
||||||
if contest_problem_id:
|
if contest_problem_id:
|
||||||
@ -274,4 +279,47 @@ def contest_page(request, contest_id):
|
|||||||
if not result["result"]:
|
if not result["result"]:
|
||||||
return render(request, "oj/contest/contest_no_privilege.html", {"contenst": contest, "reason": result["reason"]})
|
return render(request, "oj/contest/contest_no_privilege.html", {"contenst": contest, "reason": result["reason"]})
|
||||||
|
|
||||||
return render(request, "oj/contest/contest_index.html", {"contest": contest})
|
return render(request, "oj/contest/contest_index.html", {"contest": contest})
|
||||||
|
|
||||||
|
|
||||||
|
def contest_list_page(request, page=1):
|
||||||
|
# 正常情况
|
||||||
|
contests = Contest.objects.filter(visible=True)
|
||||||
|
|
||||||
|
# 搜索的情况
|
||||||
|
keyword = request.GET.get("keyword", None)
|
||||||
|
if keyword:
|
||||||
|
contests = contests.filter(title__contains=keyword)
|
||||||
|
|
||||||
|
# 筛选我能参加的比赛
|
||||||
|
join = request.GET.get("join", None)
|
||||||
|
if join:
|
||||||
|
contests = Contest.objects.filter(Q(contest_type__in=[1, 2]) | Q(groups__in=request.user.group_set.all()))
|
||||||
|
|
||||||
|
paginator = Paginator(contests, 20)
|
||||||
|
try:
|
||||||
|
current_page = paginator.page(int(page))
|
||||||
|
except Exception:
|
||||||
|
return error_page(request, u"不存在的页码")
|
||||||
|
|
||||||
|
previous_page = next_page = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
previous_page = current_page.previous_page_number()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
next_page = current_page.next_page_number()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 右侧的公告列表
|
||||||
|
announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time")
|
||||||
|
# 系统当前时间
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
return render(request, "oj/contest/contest_list.html",
|
||||||
|
{"contests": current_page, "page": int(page),
|
||||||
|
"previous_page": previous_page, "next_page": next_page,
|
||||||
|
"keyword": keyword, "announcements": announcements,
|
||||||
|
"join": join, "now": now})
|
@ -40,12 +40,15 @@ urlpatterns = [
|
|||||||
url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"),
|
url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"),
|
||||||
url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
|
url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
|
||||||
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
|
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
|
||||||
|
url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"),
|
||||||
url(r'^announcement/(?P<announcement_id>\d+)/$', "announcement.views.announcement_page",
|
url(r'^announcement/(?P<announcement_id>\d+)/$', "announcement.views.announcement_page",
|
||||||
name="announcement_page"),
|
name="announcement_page"),
|
||||||
url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"),
|
url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"),
|
||||||
name="add_contest_page"),
|
name="add_contest_page"),
|
||||||
url(r'^problems/$', "problem.views.problem_list_page", name="problem_list_page"),
|
url(r'^problems/$', "problem.views.problem_list_page", name="problem_list_page"),
|
||||||
url(r'^problems/(?P<page>\d+)/$', "problem.views.problem_list_page", name="problem_list_page"),
|
url(r'^problems/(?P<page>\d+)/$', "problem.views.problem_list_page", name="problem_list_page"),
|
||||||
|
url(r'^contests/$', "contest.views.contest_list_page", name="contest_list_page"),
|
||||||
|
url(r'^contests/(?P<page>\d+)/$', "contest.views.contest_list_page", name="contest_list_page"),
|
||||||
url(r'^admin/template/(?P<template_dir>\w+)/(?P<template_name>\w+).html$', AdminTemplateView.as_view(),
|
url(r'^admin/template/(?P<template_dir>\w+)/(?P<template_name>\w+).html$', AdminTemplateView.as_view(),
|
||||||
name="admin_template"),
|
name="admin_template"),
|
||||||
url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"),
|
url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"),
|
||||||
@ -63,6 +66,8 @@ urlpatterns = [
|
|||||||
name="join_group_request_admin_api"),
|
name="join_group_request_admin_api"),
|
||||||
url(r'^api/submission/$', SubmissionAPIView.as_view(), name="submission_api"),
|
url(r'^api/submission/$', SubmissionAPIView.as_view(), name="submission_api"),
|
||||||
url(r'^api/admin/submission/$', SubmissionAdminAPIView.as_view(), name="submission_admin_api_view"),
|
url(r'^api/admin/submission/$', SubmissionAdminAPIView.as_view(), name="submission_admin_api_view"),
|
||||||
|
url(r'^my_submissions/$', "submission.views.my_submission_list_page", name="my_submission_list_page"),
|
||||||
|
url(r'^my_submissions/(?P<page>\d+)/$', "submission.views.my_submission_list_page", name="my_submission_list_page"),
|
||||||
url(r'^api/admin/monitor/$', QueueLengthMonitorAPIView.as_view(), name="queue_length_monitor_api"),
|
url(r'^api/admin/monitor/$', QueueLengthMonitorAPIView.as_view(), name="queue_length_monitor_api"),
|
||||||
url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"),
|
url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"),
|
||||||
url(r'^api/contest/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"),
|
url(r'^api/contest/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"),
|
||||||
|
5
static/src/css/add_contest.css
Normal file
5
static/src/css/add_contest.css
Normal 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;
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
@import url("webuploader/webuploader.css");
|
@import url("webuploader/webuploader.css");
|
||||||
@import url("datetime_picker/bootstrap-datetimepicker.css");
|
@import url("datetime_picker/bootstrap-datetimepicker.css");
|
||||||
@import url("tagEditor/jquery.tag-editor.css");
|
@import url("tagEditor/jquery.tag-editor.css");
|
||||||
|
@import url("add_contest.css");
|
||||||
|
|
||||||
#loading-gif{
|
#loading-gif{
|
||||||
width: 40px;
|
width: 40px;
|
||||||
|
231
static/src/js/app/admin/contest/add_contest.js
Normal file
231
static/src/js/app/admin/contest/add_contest.js
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
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"
|
||||||
|
});
|
||||||
|
});
|
@ -1,242 +0,0 @@
|
|||||||
require(["jquery", "avalon", "editor", "uploader", "bs_alert", "datetimepicker",
|
|
||||||
"validation",],
|
|
||||||
function ($, avalon, editor, uploader, bs_alert) {
|
|
||||||
avalon.vmodels.add_contest = null;
|
|
||||||
$("#add-contest-form")
|
|
||||||
.formValidation({
|
|
||||||
framework: "bootstrap",
|
|
||||||
fields: {
|
|
||||||
name: {
|
|
||||||
validators: {
|
|
||||||
notEmpty: {
|
|
||||||
message: "请填写比赛名称"
|
|
||||||
},
|
|
||||||
stringLength: {
|
|
||||||
min: 1,
|
|
||||||
max: 30,
|
|
||||||
message: "名称不能超过30个字"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
validators: {
|
|
||||||
notEmpty: {
|
|
||||||
message: "请输入描述"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
start_time: {
|
|
||||||
validators: {
|
|
||||||
notEmpty: {
|
|
||||||
message: "请填写开始时间"
|
|
||||||
|
|
||||||
},
|
|
||||||
date: {
|
|
||||||
format: "YYYY-MM-DD h:m",
|
|
||||||
message: "请输入一个正确的日期格式"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
end_time: {
|
|
||||||
validators: {
|
|
||||||
notEmpty: {
|
|
||||||
message: "请填写结束时间"
|
|
||||||
},
|
|
||||||
date: {
|
|
||||||
format: "YYYY-MM-DD h:m",
|
|
||||||
message: "请输入一个正确的日期格式"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
validators: {
|
|
||||||
stringLength: {
|
|
||||||
min: 0,
|
|
||||||
max: 30,
|
|
||||||
message: "密码不能超过10个字符"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"problem_name[]": {
|
|
||||||
validators: {
|
|
||||||
notEmpty: {
|
|
||||||
message: "请输入题目名称"
|
|
||||||
},
|
|
||||||
stringLength: {
|
|
||||||
min: 1,
|
|
||||||
max: 30,
|
|
||||||
message: "题目不能超过30个字符"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cpu[]": {
|
|
||||||
validators: {
|
|
||||||
notEmpty: {
|
|
||||||
message: "请输入时间限制"
|
|
||||||
},
|
|
||||||
integer: {
|
|
||||||
message: "时间限制用整数表示"
|
|
||||||
},
|
|
||||||
between: {
|
|
||||||
inclusive: true,
|
|
||||||
min: 1,
|
|
||||||
max: 5000,
|
|
||||||
message: "只能在1-5000之间"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"memory[]": {
|
|
||||||
validators: {
|
|
||||||
notEmpty: {
|
|
||||||
message: "请输入内存"
|
|
||||||
},
|
|
||||||
integer: {
|
|
||||||
message: "请输入一个合法的数字"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on("success.form.fv", function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var data = {
|
|
||||||
title: vm.title, description: vm.description, start_time: vm.startTime, end_time: vm.endTime,
|
|
||||||
password: vm.password, model: vm.model, open_rank: vm.openRank, problems: []
|
|
||||||
};
|
|
||||||
for (var i = 0; i < vm.problems.length; i++) {
|
|
||||||
var problem = {
|
|
||||||
title: vm.problems[i].title, description: vm.problems[i].description,
|
|
||||||
cpu: vm.problems[i].cpu, memory: vm.problems[i].memory, samples: []
|
|
||||||
};
|
|
||||||
for (var j = 0; j < vm.problems[i].samples.length; j++) {
|
|
||||||
problem.samples.push({
|
|
||||||
input: vm.problems[i].samples[j].input,
|
|
||||||
output: vm.problems[i].samples[j].output
|
|
||||||
})
|
|
||||||
}
|
|
||||||
data.problems.push(problem);
|
|
||||||
}
|
|
||||||
console.log(data);
|
|
||||||
});
|
|
||||||
function make_id() {
|
|
||||||
var text = "";
|
|
||||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
||||||
for (var i = 0; i < 5; i++)
|
|
||||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
var upLoaderInited = false;
|
|
||||||
editor("#editor");
|
|
||||||
|
|
||||||
var vm = avalon.define({
|
|
||||||
$id: "add_contest",
|
|
||||||
title: "",
|
|
||||||
problemCount: 0,
|
|
||||||
description: "",
|
|
||||||
startTime: "",
|
|
||||||
endTime: "",
|
|
||||||
password: "",
|
|
||||||
model: "",
|
|
||||||
openRank: false,
|
|
||||||
problems: [],
|
|
||||||
problemNo: "-1",
|
|
||||||
add_problem: function () {
|
|
||||||
var problem_id = make_id();
|
|
||||||
var problem = {
|
|
||||||
id: problem_id,
|
|
||||||
title: "",
|
|
||||||
cpu: 1000,
|
|
||||||
memory: 256,
|
|
||||||
description: "",
|
|
||||||
samples: [],
|
|
||||||
visible: true,
|
|
||||||
test_case_id: "",
|
|
||||||
testCaseList: [],
|
|
||||||
hint: "",
|
|
||||||
difficulty: 0,
|
|
||||||
uploadSuccess: false
|
|
||||||
};
|
|
||||||
vm.problems.push(problem);
|
|
||||||
var id = vm.problems.length - 1;
|
|
||||||
editor("#problem-" + problem_id + "-description");
|
|
||||||
var hinteditor = editor("#problem-" + problem_id +"-hint");
|
|
||||||
$("#add-contest-form").formValidation('addField', $('[name="problem_name[]"]'));
|
|
||||||
$("#add-contest-form").formValidation('addField', $('[name="cpu[]"]'));
|
|
||||||
$("#add-contest-form").formValidation('addField', $('[name="memory[]"]'));
|
|
||||||
},
|
|
||||||
del_problem: function (problem) {
|
|
||||||
if (confirm("你确定要删除么?")) {
|
|
||||||
vm.problems.remove(problem);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toggle: function (item) {
|
|
||||||
item.visible = !item.visible;
|
|
||||||
},
|
|
||||||
add_sample: function (problem) {
|
|
||||||
problem.samples.push({id: make_id(), visible: true, input: "", output: ""});
|
|
||||||
},
|
|
||||||
del_sample: function (problem, sample) {
|
|
||||||
if (confirm("你确定要删除么?")) {
|
|
||||||
problem.samples.remove(sample);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getBtnContent: function (item) {
|
|
||||||
if (item.visible)
|
|
||||||
return "折叠";
|
|
||||||
return "展开";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
uploader("#uploader", "/api/admin/test_case_upload/", function (file, respond) {
|
|
||||||
if (respond.code)
|
|
||||||
bs_alert(respond.data);
|
|
||||||
else {
|
|
||||||
var index = parseInt(vm.problemNo)-1;
|
|
||||||
vm.problems[index].test_case_id = respond.data.test_case_id;
|
|
||||||
vm.problems[index].uploadSuccess = true;
|
|
||||||
vm.problems[index].testCaseList = [];
|
|
||||||
for (var i = 0; i < respond.data.file_list.input.length; i++) {
|
|
||||||
vm.problems[index].testCaseList.push({
|
|
||||||
input: respond.data.file_list.input[i],
|
|
||||||
output: respond.data.file_list.output[i]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
bs_alert("测试数据添加成功!共添加"+vm.problems[index].testCaseList.length +"组测试数据");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(){
|
|
||||||
console.log(vm.problemNo);
|
|
||||||
if (vm.problemNo == "-1")
|
|
||||||
{
|
|
||||||
bs_alert("你还未指定一道题目!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
isUploaderInited = 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"
|
|
||||||
});
|
|
||||||
$("#contest_start_time").datetimepicker()
|
|
||||||
.on("hide", function (ev) {
|
|
||||||
$("#add-contest-form")
|
|
||||||
.formValidation("revalidateField", "start_time");
|
|
||||||
});
|
|
||||||
$("#contest_end_time").datetimepicker()
|
|
||||||
.on("hide", function (ev) {
|
|
||||||
$("#add-contest-form")
|
|
||||||
.formValidation("revalidateField", "end_time");
|
|
||||||
});
|
|
||||||
});
|
|
137
static/src/js/app/admin/contest/contest_list.js
Normal file
137
static/src/js/app/admin/contest/contest_list.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
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请求
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
avalon.scan();
|
||||||
|
});
|
@ -1,3 +1,44 @@
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from account.models import User, REGULAR_USER
|
||||||
|
from submission.models import Submission
|
||||||
|
from rest_framework.test import APITestCase, APIClient
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
|
class SubmissionsListPageTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.client = APIClient()
|
||||||
|
self.user = User.objects.create(username="gogoing", admin_type=REGULAR_USER)
|
||||||
|
self.user2 = User.objects.create(username="cool", admin_type=REGULAR_USER)
|
||||||
|
self.user2.set_password("666666")
|
||||||
|
self.user.set_password("666666")
|
||||||
|
self.user.save()
|
||||||
|
# self.client.login(username="gogoing", password="666666")
|
||||||
|
self.submission = Submission.objects.create(user_id=self.user.id,
|
||||||
|
language=1,
|
||||||
|
code='#include "stdio.h"\nint main(){\n\treturn 0;\n}',
|
||||||
|
problem_id=1)
|
||||||
|
|
||||||
|
def test_visit_submissionsListPage_successfully(self):
|
||||||
|
self.client.login(username="gogoing", password="666666")
|
||||||
|
response = self.client.get('/my_submissions/1/')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_visit_submissionsListPage_without_page_successfully(self):
|
||||||
|
self.client.login(username="gogoing", password="666666")
|
||||||
|
response = self.client.get('/my_submissions/')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_submissionsListPage_does_not_exist(self):
|
||||||
|
self.client.login(username="gogoing", password="666666")
|
||||||
|
response = self.client.get('/my_submissions/5/')
|
||||||
|
self.assertTemplateUsed(response, "utils/error.html")
|
||||||
|
|
||||||
|
def test_submissionsListPage_page_not_exist(self):
|
||||||
|
self.client.login(username="gogoing", password="666666")
|
||||||
|
response = self.client.get('/my_submissions/5/')
|
||||||
|
self.assertTemplateUsed(response, "utils/error.html")
|
||||||
|
|
||||||
|
def test_submissionsListPage_have_no_submission(self):
|
||||||
|
self.client.login(username="cool", password="666666")
|
||||||
|
response = self.client.get('/my_submissions/')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
@ -15,6 +15,7 @@ from problem.models import Problem
|
|||||||
from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate
|
from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate
|
||||||
from .models import Submission
|
from .models import Submission
|
||||||
from .serializers import CreateSubmissionSerializer, SubmissionSerializer
|
from .serializers import CreateSubmissionSerializer, SubmissionSerializer
|
||||||
|
from django.core.paginator import Paginator
|
||||||
|
|
||||||
|
|
||||||
class SubmissionAPIView(APIView):
|
class SubmissionAPIView(APIView):
|
||||||
@ -72,7 +73,7 @@ def problem_my_submissions_list_page(request, problem_id):
|
|||||||
problem = Problem.objects.get(id=problem_id, visible=True)
|
problem = Problem.objects.get(id=problem_id, visible=True)
|
||||||
except Problem.DoesNotExist:
|
except Problem.DoesNotExist:
|
||||||
return error_page(request, u"问题不存在")
|
return error_page(request, u"问题不存在")
|
||||||
submissions = Submission.objects.filter(user_id=request.user.id, problem_id=problem.id).order_by("-create_time").\
|
submissions = Submission.objects.filter(user_id=request.user.id, problem_id=problem.id).order_by("-create_time"). \
|
||||||
values("id", "result", "create_time", "accepted_answer_time", "language")
|
values("id", "result", "create_time", "accepted_answer_time", "language")
|
||||||
return render(request, "oj/problem/my_submissions_list.html",
|
return render(request, "oj/problem/my_submissions_list.html",
|
||||||
{"submissions": submissions, "problem": problem})
|
{"submissions": submissions, "problem": problem})
|
||||||
@ -104,11 +105,34 @@ def my_submission(request, submission_id):
|
|||||||
{"submission": submission, "problem": problem, "info": info})
|
{"submission": submission, "problem": problem, "info": info})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SubmissionAdminAPIView(APIView):
|
class SubmissionAdminAPIView(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
problem_id = request.GET.get("problem_id", None)
|
problem_id = request.GET.get("problem_id", None)
|
||||||
if not problem_id:
|
if not problem_id:
|
||||||
return error_response(u"参数错误")
|
return error_response(u"参数错误")
|
||||||
submissions = Submission.objects.filter(problem_id=problem_id).order_by("-create_time")
|
submissions = Submission.objects.filter(problem_id=problem_id).order_by("-create_time")
|
||||||
return paginate(request, submissions, SubmissionSerializer)
|
return paginate(request, submissions, SubmissionSerializer)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def my_submission_list_page(request, page=1):
|
||||||
|
submissions = Submission.objects.filter(user_id=request.user.id). \
|
||||||
|
values("id", "result", "create_time", "accepted_answer_time", "language")
|
||||||
|
paginator = Paginator(submissions, 20)
|
||||||
|
try:
|
||||||
|
current_page = paginator.page(int(page))
|
||||||
|
except Exception:
|
||||||
|
return error_page(request, u"不存在的页码")
|
||||||
|
previous_page = next_page = None
|
||||||
|
try:
|
||||||
|
previous_page = current_page.previous_page_number()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
next_page = current_page.next_page_number()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return render(request, "oj/submission/my_submissions_list.html",
|
||||||
|
{"submissions": current_page, "page": int(page),
|
||||||
|
"previous_page": previous_page, "next_page": next_page, "startId":int(page)*20-20})
|
||||||
|
@ -6,7 +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>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
@ -15,6 +17,7 @@
|
|||||||
<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>
|
||||||
<small ms-visible="description==''" style="color:red">请填写比赛描述</small>
|
<small ms-visible="description==''" style="color:red">请填写比赛描述</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -27,125 +30,150 @@
|
|||||||
<div class="col-md-6">
|
<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">
|
ms-duplex="startTime" data-error="请填写比赛开始时间" required>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" name="end_time" id="contest_end_time" ms-duplex="endTime">
|
<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>
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
<div class="col-md-6">
|
<label>允许参加的用户</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6" >
|
||||||
<label>密码保护</label>
|
<label>密码保护</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-6">
|
||||||
<label>
|
<div class="form-group">
|
||||||
模式
|
<select class="form-control" name="password" ms-duplex="group" ms-change="addGroup" value="-1">
|
||||||
</label>
|
<option value="-1">请选择</option>
|
||||||
</div>
|
<option ms-repeat="groupList" ms-attr-value="$index" ms-visible="!el.chose">{{el.name}}</option>
|
||||||
<div class="col-md-3">
|
</select>
|
||||||
<label>
|
</div>
|
||||||
结束前是否开放排名
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password">
|
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password" ms-attr-readonly="!passwordUsable">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div ms-repeat="choseGroupList" class="group-tag" ms-click="unchose($index)">{{el.name}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label>排名方式</label>
|
||||||
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
|
<label>结束前是否开放排名</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label>是否公开提交记录</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label><input type="radio" name="mode" ms-duplex-checked="model">
|
<label><input type="radio" name="mode" ms-duplex-string="mode" value="0">
|
||||||
<small>OI</small>
|
|
||||||
</label>
|
|
||||||
<label><input type="radio" name="mode">
|
|
||||||
<small>ACM</small>
|
<small>ACM</small>
|
||||||
</label>
|
</label>
|
||||||
|
<label><input type="radio" name="mode" ms-duplex-string="mode" value="1">
|
||||||
|
<small>AC数量</small>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="mode" ms-duplex-string="mode" value="2">
|
||||||
|
<small>分数</small>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</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="openRank">
|
<label class="text"><input type="checkbox" ms-duplex-checked="showRank">
|
||||||
<small>开放排名</small>
|
<small>开放排名</small>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
<div class="col-md-12">
|
<div class="form-group">
|
||||||
<label>添加题目</label>
|
<label class="text"><input type="checkbox" ms-duplex-checked="showSbumission">
|
||||||
<a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="add_problem()">添加</a>
|
<small>允许查看提交记录</small>
|
||||||
|
</label>
|
||||||
<div class="col-md-12">
|
|
||||||
<label>上传测试用例</label>
|
|
||||||
<label>选择题号</label><select ms-duplex="problemNo">
|
|
||||||
<option value="-1">未指定</option>
|
|
||||||
<option ms-repeat="problems" ms-attr-value="$index+1">{{$index+1}}</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<div id="uploader">
|
|
||||||
<div>选择文件</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="problem" ms-repeat-problem="problems">
|
<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 panel-default problem-panel">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<span class="panel-title">题目{{$index + 1}} </span>
|
<span class="panel-title">题目{{editingProblemId}} </span>
|
||||||
<a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="toggle(problem)">
|
<a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="hidden()">隐藏</a>
|
||||||
{{ getBtnContent(problem)}}
|
<a href="javascript:void(0)" class="btn btn-danger btn-sm" ms-click="del_problem(editingProblemId)">删除</a>
|
||||||
</a>
|
|
||||||
<a href="javascript:void(0)" class="btn btn-danger btn-sm" ms-click="del_problem(problem)">
|
|
||||||
删除
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body" ms-visible="problem.visible">
|
<div class="panel-body" >
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
<label>题目标题</label>
|
<label>题目标题</label>
|
||||||
<input type="text" name="problem_name[]" class="form-control" ms-duplex="problem.title">
|
<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>
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
<label>题目描述</label>
|
<label>题目描述</label>
|
||||||
<textarea ms-attr-id="problem-{{ problem.id }}-description" placeholder="这里输入内容"
|
<textarea id="problemDescriptionEditor" placeholder="这里输入内容"
|
||||||
ms-duplex="problem.description"></textarea>
|
ms-duplex="problems[editingProblemId-1].description"></textarea>
|
||||||
<small ms-visible="problem.description==''" style="color:red">请填写题目描述</small>
|
<small ms-visible="editDescription==''" style="color:red">请填写题目描述</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
<label>提示</label>
|
<label>提示</label>
|
||||||
<textarea ms-attr-id="problem-{{ problem.id }}-hint" placeholder="这里输入内容"
|
<textarea id="problemHintEditor" placeholder="这里输入内容"
|
||||||
ms-duplex="problem.hint"></textarea>
|
ms-duplex="problems[editingProblemId-1].hint"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 form-group">
|
<div class="col-md-3 form-group">
|
||||||
<label>cpu</label>
|
<label>cpu</label>
|
||||||
<input type="number" name="cpu[]" class="form-control" ms-duplex="problem.cpu">
|
<input type="number" class="form-control" ms-duplex="problems[editingProblemId-1].timeLimit" data-error="请填写时间限制" required>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 form-group">
|
<div class="col-md-3 form-group">
|
||||||
<label>内存</label>
|
<label>内存</label>
|
||||||
<input type="number" name="memory[]" class="form-control" ms-duplex="problem.memory">
|
<input type="number" class="form-control" ms-duplex="problems[editingProblemId-1].memoryLimit" data-error="请填写内存限制" required>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 form-group">
|
<div class="col-md-3 form-group" ms-visible="mode==2">
|
||||||
<label>难度</label>
|
<label>分值</label>
|
||||||
<input type="number" name="difficulty[]" class="form-control"
|
<input type="number" class="form-control" ms-duplex="problems[editingProblemId-1].score" data-error="请填写题目分值" required>
|
||||||
ms-duplex="problem.difficulty">
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<label>样例</label>
|
<label>样例</label>
|
||||||
<a href="javascript:void(0)" class="btn btn-primary btn-sm"
|
<a href="javascript:void(0)" class="btn btn-primary btn-sm"
|
||||||
ms-click="add_sample(problem)">添加</a>
|
ms-click="add_sample()">添加</a>
|
||||||
|
<div>
|
||||||
<div class="sample">
|
<div class="panel panel-default sample-panel" ms-repeat-sample="editSamples">
|
||||||
<div class="panel panel-default sample-panel" ms-repeat-sample="problem.samples">
|
<div class="panel-heading">
|
||||||
<div class="panel-heading">
|
|
||||||
<span class="panel-title">样例{{$index + 1}}</span>
|
<span class="panel-title">样例{{$index + 1}}</span>
|
||||||
<a href="javascript:void(0)" class="btn btn-primary btn-sm"
|
<a href="javascript:void(0)" class="btn btn-primary btn-sm"
|
||||||
ms-click="toggle(sample)">
|
ms-click="toggle(sample)">{{ getBtnContent(sample)}}</a>
|
||||||
{{ getBtnContent(sample)}}
|
|
||||||
</a>
|
|
||||||
<a href="javascript:void(0)" class="btn btn-danger btn-sm"
|
<a href="javascript:void(0)" class="btn btn-danger btn-sm"
|
||||||
ms-click="del_sample(problem, sample)">
|
ms-click="del_sample(sample)">删除</a>
|
||||||
删除
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body row" ms-visible="sample.visible">
|
<div class="panel-body row" ms-visible="sample.visible">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@ -167,17 +195,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<label>测试数据</label><br>
|
|
||||||
<small class="text-info">请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:<br>
|
<label>测试数据</label>
|
||||||
1.in 1.out 2.in 2.out
|
<table class="table table-striped">
|
||||||
</small>
|
|
||||||
<table class="table table-striped" ms-visible="problem.uploadSuccess">
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>编号</td>
|
<td>编号</td>
|
||||||
<td>输入文件名</td>
|
<td>输入文件名</td>
|
||||||
<td>输出文件名</td>
|
<td>输出文件名</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ms-repeat="problem.testCaseList">
|
<tr ms-repeat="editTestCaseList">
|
||||||
<td>{{$index}}</td>
|
<td>{{$index}}</td>
|
||||||
<td>{{ el.input }}</td>
|
<td>{{ el.input }}</td>
|
||||||
<td>{{ el.output }}</td>
|
<td>{{ el.output }}</td>
|
||||||
@ -189,6 +215,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-12">
|
<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>
|
||||||
@ -196,5 +230,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/app/admin/contest/contest.js"></script>
|
<script src="/static/js/app/admin/contest/add_contest.js"></script>
|
||||||
<link href="/static/css/tagEditor/jquery.tag-editor.css" rel="stylesheet">
|
|
||||||
|
168
template/admin/contest/contest_list.html
Normal file
168
template/admin/contest/contest_list.html
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
<div ms-controller="contestList" class="col-md-9">
|
||||||
|
<h1>比赛列表</h1>
|
||||||
|
|
||||||
|
<div class="right">
|
||||||
|
<form class="form-inline" onsubmit="return false;">
|
||||||
|
<div class="form-group-sm">
|
||||||
|
<label>搜索</label>
|
||||||
|
<input name="keyWord" class="form-control" placeholder="请输入关键词" ms-duplex="keyword">
|
||||||
|
<input type="submit" value="搜索" class="btn btn-primary" ms-click="getPage(1)">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>比赛</th>
|
||||||
|
<th>比赛类型</th>
|
||||||
|
<th>公开排名</th>
|
||||||
|
<th>开始时间</th>
|
||||||
|
<th>结束时间</th>
|
||||||
|
<th>创建时间</th>
|
||||||
|
<th>创建者</th>
|
||||||
|
</tr>
|
||||||
|
<tr ms-repeat="contestList" ms-click="showEditContestArea($index+1)">
|
||||||
|
<td>{{ el.id }}</td>
|
||||||
|
<td>{{ el.title }}</td>
|
||||||
|
<td>{{ contestTypeNameList[el.contest_type] }}</td>
|
||||||
|
<td>{{ el.show_rank }}</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.created_by.username }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div class="text-right">
|
||||||
|
页数:{{ page }}/{{ totalPage }}
|
||||||
|
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
|
||||||
|
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
|
||||||
|
</div>
|
||||||
|
<div ms-visible="editingContestId">
|
||||||
|
<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="editTitle"
|
||||||
|
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">
|
||||||
|
<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 class="col-md-6">
|
||||||
|
<label>开始时间</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label>结束时间</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="start_time" id="contest_start_time"
|
||||||
|
ms-duplex="editStartTime" data-error="请填写比赛开始时间" required>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="end_time" id="contest_end_time"
|
||||||
|
ms-duplex="editEndTime" data-error="请填写比赛结束时间" required>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</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 class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="editPassword"
|
||||||
|
ms-attr-readonly="!passwordUsable">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div ms-repeat="choseGroupList" class="group-tag" ms-click="unchose($index)">{{el.name}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label>排名方式</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label>结束前是否开放排名</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label>是否公开提交记录</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label><input type="radio" name="mode" ms-duplex-string="editMode" value="0">
|
||||||
|
<small>ACM</small>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="mode" ms-duplex-string="editMode" value="1">
|
||||||
|
<small>AC数量</small>
|
||||||
|
</label>
|
||||||
|
<label><input type="radio" name="mode" ms-duplex-string="editMode" value="2">
|
||||||
|
<small>分数</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="text"><input type="checkbox" ms-duplex-checked="editShowRank">
|
||||||
|
<small>开放排名</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="text"><input type="checkbox" ms-duplex-checked="editShowSubmission">
|
||||||
|
<small>允许查看提交记录</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<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="editProblemList">
|
||||||
|
<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>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script src="/static/js/app/admin/contest/contest_list.js"></script>
|
12
template/oj/announcement/_announcement_panel.html
Normal file
12
template/oj/announcement/_announcement_panel.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<div class="panel panel-info">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">
|
||||||
|
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
|
||||||
|
公告
|
||||||
|
</h3></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{% for item in announcements %}
|
||||||
|
<p>{{ forloop.counter }}. <a href="/announcement/{{ item.id }}/" target="_blank">{{ item.title }}</a></p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
81
template/oj/contest/contest_list.html
Normal file
81
template/oj/contest/contest_list.html
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
{% extends "oj_base.html" %}
|
||||||
|
{% block body %}
|
||||||
|
{% load contest %}
|
||||||
|
<div class="container main">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-9">
|
||||||
|
<div class="row">
|
||||||
|
<div class="right">
|
||||||
|
<form class="form-inline" method="get">
|
||||||
|
<div class="form-group-sm">
|
||||||
|
<input name="keyword" class="form-control" placeholder="请输入关键词">
|
||||||
|
<input type="submit" value="搜索" class="btn btn-primary">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>比赛名称</th>
|
||||||
|
<th>开始时间</th>
|
||||||
|
<th>比赛类型</th>
|
||||||
|
<th>状态</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in contests %}
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><a href="/contest/{{ item.id }}/">{{ item.id }}</a></th>
|
||||||
|
<td><a href="/contest/{{ item.id }}/">{{ item.title }}</a></td>
|
||||||
|
<td>{{ item.start_time }}</td>
|
||||||
|
|
||||||
|
{% ifequal item.contest_type 0 %}
|
||||||
|
<td>小组赛</td>
|
||||||
|
{% endifequal %}
|
||||||
|
{% ifequal item.contest_type 1 %}
|
||||||
|
<td>公开赛</td>
|
||||||
|
{% endifequal %}
|
||||||
|
{% ifequal item.contest_type 2 %}
|
||||||
|
<td>公开赛(密码保护)</td>
|
||||||
|
{% endifequal %}
|
||||||
|
|
||||||
|
<td class="{{ item|contest_status_color }}">{{ item|contest_status }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>仅显示当前可参加的比赛
|
||||||
|
<input id="join" type="checkbox" {% if join %}checked{% endif %} onchange="if(this.checked){location.href='/contests/?join=True'}else{location.href='/contests/'}">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<nav>
|
||||||
|
<ul class="pager">
|
||||||
|
{% if previous_page %}
|
||||||
|
<li class="previous">
|
||||||
|
<a href="/contests/{{ previous_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}{% if join %}?join={{ join }}{% endif %}">
|
||||||
|
<span aria-hidden="true">←</span> 上一页</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if next_page %}
|
||||||
|
<li class="next">
|
||||||
|
<a href="/contests/{{ next_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}{% if join %}?join={{ join }}{% endif %}">下一页 <span
|
||||||
|
aria-hidden="true">→</span></a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3">
|
||||||
|
{% include "oj/announcement/_announcement_panel.html" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block js_block %}
|
||||||
|
{% endblock %}
|
@ -55,18 +55,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-lg-3">
|
<div class="col-lg-3">
|
||||||
<div class="panel panel-info">
|
{% include "oj/announcement/_announcement_panel.html" %}
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title">
|
|
||||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
|
|
||||||
公告
|
|
||||||
</h3></div>
|
|
||||||
<div class="panel-body">
|
|
||||||
{% for item in announcements %}
|
|
||||||
{{ forloop.counter }}. <a href="/announcement/{{ item.id }}/" target="_blank">{{ item.title }}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel panel-info">
|
<div class="panel panel-info">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h3 class="panel-title">
|
<h3 class="panel-title">
|
||||||
|
66
template/oj/submission/my_submissions_list.html
Normal file
66
template/oj/submission/my_submissions_list.html
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
{% extends 'oj_base.html' %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
{% load submission %}
|
||||||
|
<div class="container main">
|
||||||
|
<ul class="nav nav-tabs nav-tabs-google">
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="/submission/">提交</a></li>
|
||||||
|
<li role="presentation" class="active">
|
||||||
|
<a href="/submission/my_submissions/">我的提交</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
{% if submissions %}
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr class="" success>
|
||||||
|
<th>#</th>
|
||||||
|
<th>提交时间</th>
|
||||||
|
<th>结果</th>
|
||||||
|
<th>运行时间</th>
|
||||||
|
<th>语言</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in submissions %}
|
||||||
|
<tr class="{{ item.result|translate_result_class }}">
|
||||||
|
<th scope="row"><a href="/my_submission/{{ item.id }}/" id="id_{{ forloop.counter }}">
|
||||||
|
{{ forloop.counter |add:startId }}</a></th>
|
||||||
|
<td>{{ item.create_time }}</td>
|
||||||
|
<td>{{ item.result|translate_result }}</td>
|
||||||
|
<td>
|
||||||
|
{% if item.accepted_answer_time %}
|
||||||
|
{{ item.accepted_answer_time }}ms
|
||||||
|
{% else %}
|
||||||
|
--
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ item.language|translate_language }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p>你还没有提交记录!</p>
|
||||||
|
{% endif %}
|
||||||
|
<nav>
|
||||||
|
<ul class="pager">
|
||||||
|
{% if previous_page %}
|
||||||
|
<li class="previous"><a
|
||||||
|
href="/my_submissions/{{ previous_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}{% if tag %}?tag={{ tag }}{% endif %}">
|
||||||
|
<span aria-hidden="true">←</span> 上一页</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if next_page %}
|
||||||
|
<li class="next"><a
|
||||||
|
href="/my_submissions/{{ next_page }}/{% if keyword %}?keyword={{ keyword }}{% endif %}{% if tag %}?tag={{ tag }}{% endif %}">下一页 <span
|
||||||
|
aria-hidden="true">→</span></a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
29
utils/templatetags/contest.py
Normal file
29
utils/templatetags/contest.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
import datetime
|
||||||
|
from django.utils.timezone import localtime
|
||||||
|
|
||||||
|
|
||||||
|
def get_contest_status(contest):
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
if localtime(contest.start_time).replace(tzinfo=None) > now:
|
||||||
|
return "没有开始"
|
||||||
|
if localtime(contest.end_time).replace(tzinfo=None) < now:
|
||||||
|
return "已经结束"
|
||||||
|
return "正在进行"
|
||||||
|
|
||||||
|
|
||||||
|
def get_contest_status_color(contest):
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
if localtime(contest.start_time).replace(tzinfo=None) > now:
|
||||||
|
return "info"
|
||||||
|
if localtime(contest.end_time).replace(tzinfo=None) < now:
|
||||||
|
return "warning"
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
register.filter("contest_status", get_contest_status)
|
||||||
|
register.filter("contest_status_color", get_contest_status_color)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user