diff --git a/contest/views.py b/contest/views.py index 34271b1c..4ddff2c8 100644 --- a/contest/views.py +++ b/contest/views.py @@ -1,9 +1,12 @@ # coding=utf-8 import json +import datetime +from django.utils.timezone import localtime from django.shortcuts import render from django.db import IntegrityError from django.utils import dateparse from django.db.models import Q +from django.core.paginator import Paginator from rest_framework.views import APIView from utils.shortcuts import (serializer_invalid_response, error_response, 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.decorators import login_required from group.models import Group +from announcement.models import Announcement from .models import Contest, ContestProblem from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer, CreateContestProblemSerializer, ContestProblemSerializer, - EditContestProblemSerializer, ContestPasswordVerifySerializer) + EditContestProblemSerializer, ContestPasswordVerifySerializer, + EditContestProblemSerializer) class ContestAdminAPIView(APIView): @@ -200,7 +205,7 @@ class ContestProblemAdminAPIView(APIView): """ 比赛题目分页json api接口 --- - response_serializer: ProblemSerializer + response_serializer: ContestProblemSerializer """ contest_problem_id = request.GET.get("contest_problem_id", None) if contest_problem_id: @@ -274,4 +279,47 @@ def contest_page(request, contest_id): if not result["result"]: return render(request, "oj/contest/contest_no_privilege.html", {"contenst": contest, "reason": result["reason"]}) - return render(request, "oj/contest/contest_index.html", {"contest": contest}) \ No newline at end of file + 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}) \ No newline at end of file diff --git a/oj/urls.py b/oj/urls.py index 2f66950e..08b0c938 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -40,12 +40,15 @@ urlpatterns = [ 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'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), + url(r'^contest/(?P\d+)/$', "contest.views.contest_page", name="contest_page"), url(r'^announcement/(?P\d+)/$', "announcement.views.announcement_page", name="announcement_page"), url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), name="add_contest_page"), url(r'^problems/$', "problem.views.problem_list_page", name="problem_list_page"), url(r'^problems/(?P\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\d+)/$', "contest.views.contest_list_page", name="contest_list_page"), url(r'^admin/template/(?P\w+)/(?P\w+).html$', AdminTemplateView.as_view(), name="admin_template"), url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"), @@ -63,6 +66,8 @@ urlpatterns = [ name="join_group_request_admin_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'^my_submissions/$', "submission.views.my_submission_list_page", name="my_submission_list_page"), + url(r'^my_submissions/(?P\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'^contest/(?P\d+)/$', "contest.views.contest_page", name="contest_page"), url(r'^api/contest/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"), diff --git a/static/src/css/add_contest.css b/static/src/css/add_contest.css new file mode 100644 index 00000000..f9a00e67 --- /dev/null +++ b/static/src/css/add_contest.css @@ -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; +} diff --git a/static/src/css/admin.css b/static/src/css/admin.css index 9eb4f9ca..66186309 100644 --- a/static/src/css/admin.css +++ b/static/src/css/admin.css @@ -6,6 +6,7 @@ @import url("webuploader/webuploader.css"); @import url("datetime_picker/bootstrap-datetimepicker.css"); @import url("tagEditor/jquery.tag-editor.css"); +@import url("add_contest.css"); #loading-gif{ width: 40px; diff --git a/static/src/js/app/admin/contest/add_contest.js b/static/src/js/app/admin/contest/add_contest.js new file mode 100644 index 00000000..dfa8d603 --- /dev/null +++ b/static/src/js/app/admin/contest/add_contest.js @@ -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" + }); + }); diff --git a/static/src/js/app/admin/contest/contest.js b/static/src/js/app/admin/contest/contest.js deleted file mode 100644 index 736ce58c..00000000 --- a/static/src/js/app/admin/contest/contest.js +++ /dev/null @@ -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"); - }); - }); \ No newline at end of file diff --git a/static/src/js/app/admin/contest/contest_list.js b/static/src/js/app/admin/contest/contest_list.js new file mode 100644 index 00000000..a7378e14 --- /dev/null +++ b/static/src/js/app/admin/contest/contest_list.js @@ -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(); +}); diff --git a/submission/tests.py b/submission/tests.py index 7ce503c2..c31741bc 100644 --- a/submission/tests.py +++ b/submission/tests.py @@ -1,3 +1,44 @@ 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) diff --git a/submission/views.py b/submission/views.py index e92716c9..792660a8 100644 --- a/submission/views.py +++ b/submission/views.py @@ -15,6 +15,7 @@ from problem.models import Problem from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate from .models import Submission from .serializers import CreateSubmissionSerializer, SubmissionSerializer +from django.core.paginator import Paginator 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) except Problem.DoesNotExist: 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") return render(request, "oj/problem/my_submissions_list.html", {"submissions": submissions, "problem": problem}) @@ -104,11 +105,34 @@ def my_submission(request, submission_id): {"submission": submission, "problem": problem, "info": info}) - class SubmissionAdminAPIView(APIView): def get(self, request): problem_id = request.GET.get("problem_id", None) if not problem_id: return error_response(u"参数错误") submissions = Submission.objects.filter(problem_id=problem_id).order_by("-create_time") - return paginate(request, submissions, SubmissionSerializer) \ No newline at end of file + 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}) diff --git a/template/admin/contest/add_contest.html b/template/admin/contest/add_contest.html index 1d27710f..44ae98ac 100644 --- a/template/admin/contest/add_contest.html +++ b/template/admin/contest/add_contest.html @@ -6,7 +6,9 @@
- + +
@@ -15,6 +17,7 @@
+
请填写比赛描述
@@ -27,125 +30,150 @@
+ ms-duplex="startTime" data-error="请填写比赛开始时间" required> +
- + +
- -
+
+ +
+
-
- -
-
- +
+
+ +
- +
+
+
{{el.name}}
+
+
+ +
+ +
+
+ +
+
- -
-
- -
- - 添加 - -
- - - -
-
选择文件
-
+
+
+
-
+ + 添加 + + + + + + + + + + + + + +
编号题目测试数据
题目{{ $index+1 }}{{ el.title }}{{ el.testCaseList.length }}组 + 编辑 + 删除 +
+ +
+
+ +
- 题目{{$index + 1}} - - {{ getBtnContent(problem)}} - - - 删除 - + 题目{{editingProblemId}} + 隐藏 + 删除
-
+
- + +
- - 请填写题目描述 + + 请填写题目描述
- +
- + +
- + +
-
- - +
+ + +
添加 - -
-
-
+ ms-click="add_sample()">添加 +
+
+
样例{{$index + 1}} - {{ getBtnContent(sample)}} - + ms-click="toggle(sample)">{{ getBtnContent(sample)}} - 删除 - + ms-click="del_sample(sample)">删除
@@ -167,17 +195,15 @@
-
- 请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:
- 1.in 1.out 2.in 2.out -
- + + +
- + @@ -189,6 +215,14 @@
+ +
+
选择文件
+
+ 请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:
+ 1.in 1.out 2.in 2.out +

+
@@ -196,5 +230,4 @@ - - \ No newline at end of file + diff --git a/template/admin/contest/contest_list.html b/template/admin/contest/contest_list.html new file mode 100644 index 00000000..ab878ae5 --- /dev/null +++ b/template/admin/contest/contest_list.html @@ -0,0 +1,168 @@ +
+

比赛列表

+ +
+
+
+ + + +
+ +
+
+
编号 输入文件名 输出文件名
{{$index}} {{ el.input }} {{ el.output }}
+ + + + + + + + + + + + + + + + + + + + +
ID比赛比赛类型公开排名开始时间结束时间创建时间创建者
{{ el.id }}{{ el.title }}{{ contestTypeNameList[el.contest_type] }}{{ el.show_rank }}{{ el.start_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.end_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.created_by.username }}
+
+ 页数:{{ page }}/{{ totalPage }}   + + +
+
+
+ +
+
+
+ + +
+
+
+
+ +
+
+
+ +
+ 请填写比赛描述 +
+
+
+ +
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+
{{el.name}}
+
+
+ +
+
+ +
+
+ +
+
+
+ + + +
+
+
+
+ +
+
+
+
+ +
+
+
+ + 添加 + + + + + + + + + + + + + +
编号题目测试数据
题目{{ $index+1 }}{{ el.title }}{{ el.testCaseList.length }}组 + 编辑 + 删除 +
+ +
+
+ +
+ diff --git a/template/oj/announcement/_announcement_panel.html b/template/oj/announcement/_announcement_panel.html new file mode 100644 index 00000000..f17a9ceb --- /dev/null +++ b/template/oj/announcement/_announcement_panel.html @@ -0,0 +1,12 @@ +
+
+

+ + 公告 +

+
+ {% for item in announcements %} +

{{ forloop.counter }}.  {{ item.title }}

+ {% endfor %} +
+
\ No newline at end of file diff --git a/template/oj/contest/contest_list.html b/template/oj/contest/contest_list.html new file mode 100644 index 00000000..1689cee3 --- /dev/null +++ b/template/oj/contest/contest_list.html @@ -0,0 +1,81 @@ +{% extends "oj_base.html" %} +{% block body %} + {% load contest %} +
+
+
+
+
+
+
+ + +
+
+
+
+
+ + + + + + + + + + + + {% for item in contests %} + + + + + + {% ifequal item.contest_type 0 %} + + {% endifequal %} + {% ifequal item.contest_type 1 %} + + {% endifequal %} + {% ifequal item.contest_type 2 %} + + {% endifequal %} + + + + {% endfor %} + +
#比赛名称开始时间比赛类型状态
{{ item.id }}{{ item.title }}{{ item.start_time }}小组赛公开赛公开赛(密码保护){{ item|contest_status }}
+
+ +
+ +
+
+ +
+ {% include "oj/announcement/_announcement_panel.html" %} +
+
+
+{% endblock %} + + +{% block js_block %} +{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/problem_list.html b/template/oj/problem/problem_list.html index 073cfe7b..21833a77 100644 --- a/template/oj/problem/problem_list.html +++ b/template/oj/problem/problem_list.html @@ -55,18 +55,7 @@
-
-
-

- - 公告 -

-
- {% for item in announcements %} - {{ forloop.counter }}.  {{ item.title }} - {% endfor %} -
-
+ {% include "oj/announcement/_announcement_panel.html" %}

diff --git a/template/oj/submission/my_submissions_list.html b/template/oj/submission/my_submissions_list.html new file mode 100644 index 00000000..c6fcacb0 --- /dev/null +++ b/template/oj/submission/my_submissions_list.html @@ -0,0 +1,66 @@ +{% extends 'oj_base.html' %} + +{% block body %} + +{% load submission %} +
+ + +
+ {% if submissions %} + + + + + + + + + + + + {% for item in submissions %} + + + + + + + + {% endfor %} + + +
#提交时间结果运行时间语言
+ {{ forloop.counter |add:startId }}{{ item.create_time }}{{ item.result|translate_result }} + {% if item.accepted_answer_time %} + {{ item.accepted_answer_time }}ms + {% else %} + -- + {% endif %} + + {{ item.language|translate_language }} +
+ {% else %} +

你还没有提交记录!

+ {% endif %} + +
+{% endblock %} diff --git a/utils/templatetags/contest.py b/utils/templatetags/contest.py new file mode 100644 index 00000000..818d7f6b --- /dev/null +++ b/utils/templatetags/contest.py @@ -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) +