diff --git a/contest/migrations/0005_contestproblem_score.py b/contest/migrations/0005_contestproblem_score.py new file mode 100644 index 0000000..e0a025d --- /dev/null +++ b/contest/migrations/0005_contestproblem_score.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('contest', '0004_remove_contestproblem_difficulty'), + ] + + operations = [ + migrations.AddField( + model_name='contestproblem', + name='score', + field=models.IntegerField(default=0), + ), + ] diff --git a/contest/migrations/0006_merge.py b/contest/migrations/0006_merge.py new file mode 100644 index 0000000..9a4fe59 --- /dev/null +++ b/contest/migrations/0006_merge.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('contest', '0005_contestsubmission'), + ('contest', '0005_contestproblem_score'), + ] + + operations = [ + ] diff --git a/contest/models.py b/contest/models.py index de9f2e1..338e3b1 100644 --- a/contest/models.py +++ b/contest/models.py @@ -55,6 +55,7 @@ class ContestProblem(AbstractProblem): contest = models.ForeignKey(Contest) # 比如A B 或者1 2 或者 a b 将按照这个排序 sort_index = models.CharField(max_length=30) + score = models.IntegerField(default=0) class Meta: db_table = "contest_problem" diff --git a/contest/serializers.py b/contest/serializers.py index 23d8474..a9f33ee 100644 --- a/contest/serializers.py +++ b/contest/serializers.py @@ -70,11 +70,11 @@ class CreateContestProblemSerializer(serializers.Serializer): time_limit = serializers.IntegerField() memory_limit = serializers.IntegerField() hint = serializers.CharField(max_length=3000, allow_blank=True) + score = serializers.IntegerField(required=False, default=0) sort_index = serializers.CharField(max_length=30) class ContestProblemSerializer(serializers.ModelSerializer): - class ContestSerializer(serializers.ModelSerializer): class Meta: model = Contest @@ -101,10 +101,9 @@ class EditContestProblemSerializer(serializers.Serializer): hint = serializers.CharField(max_length=3000, allow_blank=True) visible = serializers.BooleanField() sort_index = serializers.CharField(max_length=30) + score = serializers.IntegerField(required=False, default=0) class ContestPasswordVerifySerializer(serializers.Serializer): contest_id = serializers.IntegerField() password = serializers.CharField(max_length=30) - - diff --git a/contest/tests.py b/contest/tests.py index 6cce9e8..58c9127 100644 --- a/contest/tests.py +++ b/contest/tests.py @@ -28,6 +28,9 @@ class ContestAdminAPITest(APITestCase): self.group = Group.objects.create(name="group1", description="des0", join_group_setting=0, visible=True, admin=user2) + self.group2 = Group.objects.create(name="group2", description="des0", + join_group_setting=0, visible=True, + admin=user1) self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, contest_type=2, show_rank=True, show_user_submission=True, start_time="2015-08-15T10:00:00.000Z", @@ -78,7 +81,7 @@ class ContestAdminAPITest(APITestCase): response = self.client.post(self.url, data=data) self.assertEqual(response.data["code"], 0) - def test_group_contest_successfully(self): + def test_group_contest_super_admin_successfully(self): self.client.login(username="test1", password="testaa") data = {"title": "title3", "description": "description3", "mode": 1, "contest_type": 0, "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z", @@ -86,6 +89,14 @@ class ContestAdminAPITest(APITestCase): response = self.client.post(self.url, data=data) self.assertEqual(response.data["code"], 0) + def test_group_contest_admin_successfully(self): + self.client.login(username="test2", password="testbb") + data = {"title": "title6", "description": "description6", "mode": 2, "contest_type": 0, + "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z", + "end_time": "2015-08-15T12:00:00.000Z", "groups": [self.group.id], "visible": True} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 0) + def test_time_error(self): self.client.login(username="test1", password="testaa") data = {"title": "title2", "description": "description2", "mode": 1, "contest_type": 2, @@ -141,6 +152,15 @@ class ContestAdminAPITest(APITestCase): self.assertEqual(response.data["data"]["end_time"], "2015-08-15T13:00:00Z") self.assertEqual(response.data["data"]["visible"], False) + def test_edit_group_contest_unsuccessfully(self): + self.client.login(username="test2", password="testbb") + data = {"id": self.group_contest.id, "title": "titleyyy", "description": "descriptionyyyy", "mode": 1, + "contest_type": 0, "show_rank": True, "show_user_submission": True, + "start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T13:00:00.000Z", + "groups": [self.group.id], "visible": False} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 1) + def test_edit_group_at_least_one(self): self.client.login(username="test1", password="testaa") data = {"id": self.group_contest.id, "title": "titleyyy", "description": "descriptionyyyy", "mode": 1, @@ -169,7 +189,8 @@ class ContestAdminAPITest(APITestCase): def test_edit_global_contest_password_exists(self): self.client.login(username="test1", password="testaa") - data = {"id": self.global_contest.id, "title": "title0", "description": "description0", "mode": 1, "contest_type": 2, + data = {"id": self.global_contest.id, "title": "title0", "description": "description0", "mode": 1, + "contest_type": 2, "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T12:00:00.000Z", "visible": True} response = self.client.put(self.url, data=data) @@ -189,6 +210,10 @@ class ContestAdminAPITest(APITestCase): self.client.login(username="test1", password="testaa") self.assertEqual(self.client.get(self.url).data["code"], 0) + def test_get_data_successfully_by_normal_admin(self): + self.client.login(username="test2", password="testbb") + self.assertEqual(self.client.get(self.url).data["code"], 0) + def test_keyword_contest(self): self.client.login(username="test1", password="testaa") response = self.client.get(self.url + "?visible=true") @@ -209,24 +234,30 @@ class ContestProblemAdminAPItEST(APITestCase): self.user = User.objects.create(username="test1", admin_type=SUPER_ADMIN) self.user.set_password("testaa") self.user.save() + self.user2 = User.objects.create(username="test2", admin_type=ADMIN) + self.user2.set_password("testaa") + self.user2.save() + self.user3 = User.objects.create(username="test3", admin_type=ADMIN) + self.user3.set_password("testaa") + self.user3.save() self.client.login(username="test1", password="testaa") self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1, contest_type=2, show_rank=True, show_user_submission=True, start_time="2015-08-15T10:00:00.000Z", end_time="2015-08-15T12:00:00.000Z", password="aacc", created_by=User.objects.get(username="test1")) - self. contest_problem = ContestProblem.objects.create(title="titlex", - description="descriptionx", - input_description="input1_description", - output_description="output1_description", - test_case_id="1", - samples=json.dumps([{"input": "1 1", "output": "2"}]), - time_limit=100, - memory_limit=1000, - hint="hint1", - created_by=User.objects.get(username="test1"), - contest=Contest.objects.get(title="titlex"), - sort_index="a") + self.contest_problem = ContestProblem.objects.create(title="titlex", + description="descriptionx", + input_description="input1_description", + output_description="output1_description", + test_case_id="1", + samples=json.dumps([{"input": "1 1", "output": "2"}]), + time_limit=100, + memory_limit=1000, + hint="hint1", + created_by=User.objects.get(username="test1"), + contest=Contest.objects.get(title="titlex"), + sort_index="a") # 以下是发布比赛题目的测试 def test_invalid_format(self): @@ -311,6 +342,10 @@ class ContestProblemAdminAPItEST(APITestCase): self.client.login(username="test1", password="testaa") self.assertEqual(self.client.get(self.url).data["code"], 0) + def test_get_data_unsuccessfully(self): + self.client.login(username="test1", password="testaa") + self.assertEqual(self.client.get(self.url+"?contest_id=12").data["code"], 1) + def test_keyword_contest(self): self.client.login(username="test1", password="testaa") response = self.client.get(self.url + "?visible=true") @@ -333,4 +368,33 @@ class ContestProblemAdminAPItEST(APITestCase): response = self.client.get(self.url, data=data) self.assertEqual(response.data["code"], 0) + def test_query_contest_problem_exists_by_contest_id(self): + self.client.login(username="test3", password="testaa") + response = self.client.get(self.url + "?contest_id=1") + self.assertEqual(response.data["code"], 0) + self.assertEqual(len(response.data["data"]), 0) + + def test_query_contest_problem_exists_by_normal_admin(self): + self.client.login(username="test2", password="testaa") + data = {"contest_problem_id": self.contest_problem.id} + response = self.client.get(self.url, data=data) + self.assertEqual(response.data["code"], 0) + + def test_edit_problem_unsuccessfully_can_not_access(self): + self.client.login(username="test2", password="testaa") + data = {"id": self.contest_problem.id, + "title": "title2222222", + "description": "description22222222", + "input_description": "input_description2", + "output_description": "output_description2", + "test_case_id": "1", + "source": "source1", + "samples": [{"input": "1 1", "output": "2"}], + "time_limit": "100", + "memory_limit": "1000", + "hint": "hint1", + "sort_index": "b", + "visible": True} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 1) diff --git a/contest/views.py b/contest/views.py index 8134dca..65b9c05 100644 --- a/contest/views.py +++ b/contest/views.py @@ -2,17 +2,18 @@ import json import datetime from functools import wraps +from collections import OrderedDict from django.utils.timezone import now from django.shortcuts import render from django.db import IntegrityError from django.utils import dateparse -from django.db.models import Q +from django.db.models import Q, Count, Sum from django.core.paginator import Paginator from rest_framework.views import APIView from utils.shortcuts import (serializer_invalid_response, error_response, success_response, paginate, rand_str, error_page) -from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN +from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN, User from account.decorators import login_required from group.models import Group from announcement.models import Announcement @@ -279,15 +280,21 @@ def contest_problem_page(request, contest_id, contest_problem_id): contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True) except ContestProblem.DoesNotExist: return error_page(request, u"比赛题目不存在") + warning = "您已经提交过本题的正确答案!" show_warning = False try: submission = ContestSubmission.objects.get(user=request.user, contest=contest, problem=contest_problem) show_warning = submission.ac except ContestSubmission.DoesNotExist: pass - return render(request, "oj/contest/contest_problem.html", {"contest_problem": contest_problem, + + # 已经结束 + if contest.status == -1: + show_warning = True + warning = "比赛已经结束!" + return render(request, "oj/contest/contest_problem.html", {"contest_problem": contest_problem, "contest": contest, "samples": json.loads(contest_problem.samples), - "show_warning": show_warning}) + "show_warning": show_warning, "warning": warning}) @check_user_contest_permission @@ -353,4 +360,55 @@ def contest_list_page(request, page=1): +def _cmp(x, y): + if x["total_ac"] > y["total_ac"]: + return 1 + elif x["total_ac"] < y["total_ac"]: + return -1 + else: + if x["total_time"] < y["total_time"]: + return 1 + else: + return -1 + + +@check_user_contest_permission +def contest_rank_page(request, contest_id): + result = ContestSubmission.objects.values("user_id").annotate(total_submit=Count("user_id")) + for i in range(0, len(result)): + # 这个人所有的提交 + submissions = ContestSubmission.objects.filter(user_id=result[i]["user_id"]) + result[i]["total_ac"] = submissions.filter(ac=True).count() + result[i]["user"] = User.objects.get(id=result[i]["user_id"]) + result[i]["submissions"] = submissions.order_by("problem__sort_index") + result[i]["total_time"] = submissions.filter(ac=True).aggregate(total_time=Sum("total_time"))["total_time"] + print result + + return render(request, "oj/contest/contest_rank.html") + + # + # + # return + # contest = Contest.objects.get(id=contest_id) + # contest_submissions = ContestSubmission.objects.filter(contest=contest) + # result = {} + # # 先把数据按照用户id 为 key 整理一下 + # # {1: {"submissions": [], "total_time": 0, "total_ac": 0}} + # for item in contest_submissions: + # if item.user.id not in contest_submissions: + # result[item.user.id] = {"user": {"id": item.user.id, "username": item.user.username, + # "real_name": item.user.real_name}, + # "submissions": [], "total_time": 0, "total_ac": 0} + # result[item.user.id]["submissions"].append(ContestSubmissionSerializer(item).data) + # if item.ac: + # result[item.user.id]["total_time"] += item.total_time + # result[item.user.id]["total_ac"] += 1 + # l = [] + # for k, v in result.iteritems(): + # l.append(v) + # print sorted(l, cmp=_cmp, reverse=True) + + + + diff --git a/static/src/css/contest/add_contest.css b/static/src/css/contest/add_contest.css new file mode 100644 index 0000000..af9ee38 --- /dev/null +++ b/static/src/css/contest/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/js/app/admin/admin.js b/static/src/js/app/admin/admin.js index 989cbf7..bd0d51e 100644 --- a/static/src/js/app/admin/admin.js +++ b/static/src/js/app/admin/admin.js @@ -75,6 +75,9 @@ define("admin", ["jquery", "avalon"], function ($, avalon) { groupId: -1, problemId: -1, adminNavList: [], + $contestMode: -1, + $problemId: -1, + $contestId: -1, hide_loading: function () { $("#loading-gif").hide(); }, @@ -123,6 +126,18 @@ define("admin", ["jquery", "avalon"], function ($, avalon) { vm.template_url = "template/problem/submission_list.html"; }); + vm.$watch("showContestProblemPage", function (problemId, contestId, contestMode) { + vm.$problemId = problemId; + vm.$contestId = contestId; + vm.$contestMode = contestMode + vm.template_url = "template/contest/edit_problem.html"; + }); + + vm.$watch("showContestListPage", function (problemId) { + vm.problemId = problemId; + vm.template_url = "template/contest/contest_list.html"; + }); + avalon.scan(); window.onhashchange = function () { diff --git a/static/src/js/app/admin/contest/addContest.js b/static/src/js/app/admin/contest/addContest.js new file mode 100644 index 0000000..437fbdf --- /dev/null +++ b/static/src/js/app/admin/contest/addContest.js @@ -0,0 +1,166 @@ +require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "datetimePicker", + "validator"], + function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { + + //avalon.vmodels.add_contest = null; + $("#add-contest-form").validator().on('submit', function (e) { + if (!e.isDefaultPrevented()){ + e.preventDefault(); + var ajaxData = { + title: vm.title, + description: vm.description, + mode: vm.mode, + contest_type: 0, + show_rank: vm.showRank, + show_user_submission: vm.showSubmission, + start_time: vm.startTime, + end_time: vm.endTime, + visible: false + }; + if (vm.choseGroupList.length == 0) { + bsAlert("你没有选择参赛用户!"); + return false; + } + if (vm.choseGroupList[0].id == 0) { //everyone | public contest + if (vm.password) { + ajaxData.password = vm.password; + ajaxData.contest_type = 2; + } + else{ + ajaxData.contest_type = 1; + } + } + else { // Add groups info + ajaxData.groups = []; + for (var i = 0; vm.choseGroupList[i]; i++) + ajaxData.groups.push(parseInt(vm.choseGroupList[i].id)) + } + + $.ajax({ // Add contest + beforeSend: csrfTokenHeader, + url: "/api/admin/contest/", + dataType: "json", + contentType: "application/json", + data: JSON.stringify(ajaxData), + method: "post", + contentType: "application/json", + success: function (data) { + if (!data.code) { + bsAlert("添加成功!将转到比赛列表页以便为比赛添加问题(注意比赛当前状态为:隐藏)"); + vm.title = ""; + vm.description = ""; + vm.startTime = ""; + vm.endTime = ""; + vm.password = ""; + vm.mode = ""; + vm.showRank = false; + vm.showSubmission = false; + vm.group = "-1"; + vm.groupList = []; + vm.choseGroupList = []; + vm.passwordUsable = false; + location.hash = "#contest/contest_list"; + } + else { + bsAlert(data.data); + } + } + }); + } + return false; + }); + + editor("#editor"); + if (avalon.vmodels.add_contest) + var vm = avalon.vmodels.add_contest; + else + var vm = avalon.define({ + $id: "add_contest", + title: "", + description: "", + startTime: "", + endTime: "", + password: "", + mode: "", + showRank: false, + showSubmission: false, + group: "-1", + groupList: [], + choseGroupList: [], + passwordUsable: false, + addGroup: function() { + if (vm.group == -1) return; + if (vm.groupList[vm.group].id == 0){ + vm.passwordUsable = true; + vm.choseGroupList = []; + for (var key in vm.groupList){ + vm.groupList[key].chose = true; + } + } + vm.groupList[vm.group]. chose = true; + vm.choseGroupList.push({name:vm.groupList[vm.group].name, index:vm.group, id:vm.groupList[vm.group].id}); + vm.group = -1; + }, + removeGroup: function(groupIndex){ + if (vm.groupList[vm.choseGroupList[groupIndex].index].id == 0){ + vm.passwordUsable = false; + for (key in vm.groupList){ + vm.groupList[key].chose = false; + } + } + vm.groupList[vm.choseGroupList[groupIndex].index].chose = false; + vm.choseGroupList.remove(vm.choseGroupList[groupIndex]); + } + }); + + $.ajax({ // Get current user type + url: "/api/user/", + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + if (data.data.admin_type == 2) { // Is super user + vm.isGlobal = true; + vm.groupList.push({id:0,name:"所有人",chose:false}); + } + $.ajax({ // Get the group list of current user + beforeSend: csrfTokenHeader, + url: "/api/admin/group/", + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + if (!data.data.length) { + bsAlert("您的用户权限只能创建组内比赛,但是您还没有创建过小组"); + return; + } + for (var i = 0; i < data.data.length; i++) { + var item = data.data[i]; + item["chose"] = false; + vm.groupList.push(item); + } + } + else { + bsAlert(data.data); + } + } + }); + } + } + }); + + avalon.scan(); + + $("#contest_start_time").datetimepicker({ + format: "yyyy-mm-dd hh:ii", + minuteStep: 5, + weekStart: 1, + language: "zh-CN" + }); + $("#contest_end_time").datetimepicker({ + format: "yyyy-mm-dd hh:ii", + minuteStep: 5, + weekStart: 1, + language: "zh-CN" + }); + }); \ No newline at end of file diff --git a/static/src/js/app/admin/contest/add_contest.js b/static/src/js/app/admin/contest/add_contest.js deleted file mode 100644 index dfa8d60..0000000 --- a/static/src/js/app/admin/contest/add_contest.js +++ /dev/null @@ -1,231 +0,0 @@ -require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "datetimePicker", - "validator"], - function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { - avalon.vmodels.add_contest = null; - $("#add-contest-form").validator().on('submit', function (e) { - if (!e.isDefaultPrevented()){ - e.preventDefault(); - var ajaxData = { - title: vm.title, - description: vm.description, - mode: vm.mode, - contest_type: 0, - show_rank: vm.showRank, - show_user_submission: vm.showSubmission, - //password: vm.password, - start_time: vm.startTime, - end_time: vm.endTime, - visible: true - }; - if (vm.choseGroupList[0].id == 0) //everyone | public contest - if (vm.password == "") - ajaxData.contest_type = 1; - else{ - ajaxData.password = vm.password; - } - else { // Add groups info - ajaxData.groups = []; - for (var i = 0; vm.choseGroupList[i]; i++) - ajaxData.groups.push(parseInt(vm.choseGroupList[i].id)) - } - - - console.log(ajaxData); - $.ajax({ - beforeSend: csrfTokenHeader, - url: "/api/admin/contest/", - dataType: "json", - contentType: "application/json", - data: JSON.stringify(ajaxData), - method: "post", - contentType: "application/json", - success: function (data) { - if (!data.code) { - bsAlert("添加成功!"); - console.log(data); - } - else { - bsAlert(data.data); - console.log(data); - } - } - }); - console.log(JSON.stringify(ajaxData)); - } - return false; - }); - - editor("#editor"); - editor("#problemDescriptionEditor"); - editor("#problemHintEditor"); - - var vm = avalon.define({ - $id: "add_contest", - title: "", - description: "", - startTime: "", - endTime: "", - password: "", - mode: "", - showRank: false, - showSubmission: false, - problems: [], - editingProblemId: 0, - editSamples: [], - editTestCaseList: [], - group: "-1", - groupList: [], - choseGroupList: [], - showProblemEditArea: function (problemIndex) { - if (vm.editingProblemId == problemIndex){ - vm.problems[vm.editingProblemId-1].samples = vm.editSamples; - vm.editingProblemId = 0; - } - else { - if (vm.editingProblemId) - { - vm.problems[vm.editingProblemId-1].samples = vm.editSamples; - vm.problems[vm.editingProblemId-1].testCaseList = vm.editTestCaseList; - } - vm.editingProblemId = problemIndex; - vm.editSamples = []; - vm.editSamples = vm.problems[vm.editingProblemId-1].samples; - vm.editTestCaseList = []; - vm.editTestCaseList = vm.problems[vm.editingProblemId-1].testCaseList; - } - }, - passwordUsable: false, - add_problem: function () { - var problem = { - title: "", - timeLimit: 1000, - memoryLimit: 256, - description: "", - samples: [], - visible: true, - test_case_id: "", - testCaseList: [], - hint: "", - score: 0, - uploadSuccess: false, - }; - vm.problems.push(problem); - vm.showProblemEditArea(vm.problems.length); - }, - del_problem: function (problemIndex) { - if (confirm("你确定要删除么?")) { - vm.editingProblemId = 0; - vm.problems.remove(vm.problems[problemIndex-1]); - } - }, - hidden: function () { - vm.problems[vm.editingProblemId-1].samples = editSamples; - vm.problems[vm.editingProblemId-1].testCaseList = editTestCaseList; - vm.editingProblemId = 0; - }, - toggle: function (item) { - item.visible = !item.visible; - }, - add_sample: function () { - vm.editSamples.push({visible: true, input: "", output: ""}); - }, - del_sample: function (sample) { - if (confirm("你确定要删除么?")) { - editSamples.remove(sample); - } - }, - getBtnContent: function (item) { - if (item.visible) - return "折叠"; - return "展开"; - }, - addGroup: function() { - if (vm.group == -1) return; - if (vm.groupList[vm.group].id == 0){ - vm.passwordUsable = true; - vm.choseGroupList = []; - for (var key in vm.groupList){ - vm.groupList[key].chose = true; - } - } - vm.groupList[vm.group]. chose = true; - vm.choseGroupList.push({name:vm.groupList[vm.group].name, index:vm.group, id:vm.groupList[vm.group].id}); - }, - unchose: function(groupIndex){ - if (vm.groupList[vm.choseGroupList[groupIndex].index].id == 0){ - vm.passwordUsable = false; - for (key in vm.groupList){ - vm.groupList[key].chose = false; - } - } - vm.groupList[vm.choseGroupList[groupIndex].index].chose = false; - vm.choseGroupList.remove(vm.choseGroupList[groupIndex]); - } - }); - - var isSuperAdmin = true; - $.ajax({ //用于获取该用户创建的所有小组的ajax请求 - beforeSend: csrfTokenHeader, - url: "/api/admin/group/?my_group=true", - dataType: "json", - method: "get", - contentType: "application/json", - success: function (data) { - if (!data.code) { - if (isSuperAdmin) - vm.groupList.push({id:0, name:"所有人", chose: false}); - for (var key in data.data) { - data.data[key].chose = false; - vm.groupList.push(data.data[key]); - } - } - else { - bsAlert(data.data); - console.log(data); - } - } - }); - - - - uploader("#uploader", "/api/admin/test_case_upload/", function (file, respond) { - if (respond.code) - bsAlert(respond.data); - else { - vm.problems[vm.editingProblemId-1].test_case_id = respond.data.test_case_id; - vm.problems[vm.editingProblemId-1].uploadSuccess = true; - vm.editTestCaseList = []; - for (var i = 0; i < respond.data.file_list.input.length; i++) { - vm.editTestCaseList.push({ - input: respond.data.file_list.input[i], - output: respond.data.file_list.output[i] - }); - } - vm.problems[vm.editingProblemId-1].testCaseList = vm.editTestCaseList; - bsAlert("测试数据添加成功!共添加"+vm.editTestCaseList.length +"组测试数据"); - } - }, - function(){ - if (vm.editingProblemId == 0) - { - bsAlert("你还未指定一道题目!"); - return false; - } - } - ); - - avalon.scan(); - - $("#contest_start_time").datetimepicker({ - format: "yyyy-mm-dd hh:ii", - minuteStep: 5, - weekStart: 1, - language: "zh-CN" - }); - $("#contest_end_time").datetimepicker({ - format: "yyyy-mm-dd hh:ii", - minuteStep: 5, - weekStart: 1, - language: "zh-CN" - }); - }); diff --git a/static/src/js/app/admin/contest/contestList.js b/static/src/js/app/admin/contest/contestList.js new file mode 100644 index 0000000..9c1ba71 --- /dev/null +++ b/static/src/js/app/admin/contest/contestList.js @@ -0,0 +1,331 @@ +require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", "validator"], function ($, avalon, csrfTokenHeader, bsAlert, editor) { + + avalon.ready(function () { + + $("#edit-contest-form").validator().on('submit', function (e) { + if (!e.isDefaultPrevented()){ + e.preventDefault(); + var ajaxData = { + id: vm.contestList[vm.editingContestId-1].id, + title: vm.editTitle, + description: vm.editDescription, + mode: vm.editMode, + contest_type: 0, + show_rank: vm.editShowRank, + show_user_submission: vm.editShowSubmission, + start_time: vm.editStartTime, + end_time: vm.editEndTime, + visible: vm.editVisible + }; + if (vm.choseGroupList.length == 0) { + bsAlert("你没有选择参赛用户!"); + return false; + } + if (vm.editDescription == "") { + bsAlert("比赛描述不能为空!"); + return false; + } + if (vm.choseGroupList[0].id == 0) { //everyone | public contest + if (vm.editPassword) { + ajaxData.password = vm.editPassword; + ajaxData.contest_type = 2; + } + else{ + ajaxData.contest_type = 1; + } + } + else { // Add groups info + ajaxData.groups = []; + for (var i = 0; vm.choseGroupList[i]; i++) + ajaxData.groups.push(parseInt(vm.choseGroupList[i].id)) + } + + $.ajax({ // Add contest + beforeSend: csrfTokenHeader, + url: "/api/admin/contest/", + dataType: "json", + contentType: "application/json", + data: JSON.stringify(ajaxData), + method: "put", + contentType: "application/json", + success: function (data) { + if (!data.code) { + bsAlert("修改成功!"); + vm.editingContestId = 0; // Hide the editor + vm.getPage(1); // Refresh the contest list + } + else { + bsAlert(data.data); + } + } + }); + } + return false; + }); + + if(avalon.vmodels.contestList){ + // this page has been loaded before, so set the default value + var vm = avalon.vmodels.contestList; + vm.contestList= []; + vm.previousPage= 0; + vm.nextPage= 0; + vm.page= 1; + vm.totalPage= 1; + vm.group= "-1"; + vm.groupList= []; + vm.choseGroupList= []; + vm.passwordUsable= false; + vm.keyword= ""; + vm.editingContestId= 0; + vm.editTitle= ""; + vm.editDescription= ""; + vm.editProblemList= []; + vm.editPassword= ""; + vm.editStartTime= ""; + vm.editEndTime= ""; + vm.editMode= ""; + vm.editShowRank= false; + vm.editShowSubmission= false; + vm.editProblemList= []; + vm.editVisible= false; + vm.editChoseGroupList= []; + vm.editingProblemContestIndex= 0; + } + else { + var vm = avalon.define({ + $id: "contestList", + contestList: [], + previousPage: 0, + nextPage: 0, + page: 1, + totalPage: 1, + showVisibleOnly: false, + group: "-1", + groupList: [], + choseGroupList: [], + passwordUsable: false, + keyword: "", + editingContestId: 0, + editTitle: "", + editDescription: "", + editProblemList: [], + editPassword: "", + editStartTime: "", + editEndTime: "", + editMode: "", + editShowRank: false, + editShowSubmission: false, + editProblemList: [], + editVisible: false, + editChoseGroupList: [], + editingProblemContestIndex: 0, + getNext: function () { + if (!vm.nextPage) + return; + getPageData(vm.page + 1); + }, + getPrevious: function () { + if (!vm.previousPage) + return; + getPageData(vm.page - 1); + }, + getBtnClass: function (btn) { + if (btn == "next") { + return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; + } + else { + return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; + } + }, + getPage: function (page_index) { + getPageData(page_index); + }, + showEditContestArea: function (contestId) { + if (vm.editingContestId && !confirm("如果继续将丢失未保存的信息,是否继续?")) + return; + if (contestId == vm.editingContestId) + vm.editingContestId = 0; + else { + vm.editingContestId = contestId; + vm.editTitle = vm.contestList[contestId-1].title; + vm.editPassword = vm.contestList[contestId-1].password; + vm.editStartTime = vm.contestList[contestId-1].start_time.substring(0,16).replace("T"," "); + vm.editEndTime = vm.contestList[contestId-1].end_time.substring(0,16).replace("T"," "); + vm.editMode = vm.contestList[contestId-1].mode; + vm.editVisible = vm.contestList[contestId-1].visible; + if (vm.contestList[contestId-1].contest_type == 0) { //contest type == 0, contest in group + //Clear the choseGroupList + while (vm.choseGroupList.length) { + vm.removeGroup(0); + } + + for (var i = 0; i < vm.contestList[contestId-1].groups.length; i++){ + var id = parseInt(vm.contestList[contestId-1].groups[i]); + var index = 0; + for (; vm.groupList[index]; index++) { + if (vm.groupList[index].id == id) + break; + } + vm.groupList[index].chose = true; + vm.choseGroupList.push({ + name:vm.groupList[index].name, + index:index, + id:id + }); + } + } + else{ + vm.group = "0"; + vm.addGroup()//vm.editChoseGroupList = [0]; id 0 is for the group of everyone~ + } + vm.editShowRank = vm.contestList[contestId-1].show_rank; + vm.editShowSubmission = vm.contestList[contestId-1].show_user_submission; + editor("#editor").setValue(vm.contestList[contestId-1].description); + vm.editingProblemContestIndex = 0; + } + }, + showEditProblemArea: function(contestId) { + if (vm.editingProblemContestIndex == contestId) { + vm.editingProblemContestIndex = 0; + return; + } + if (vm.editingContestId&&!confirm("如果继续将丢失未保存的信息,是否继续?")){ + return; + } + $.ajax({ // Get the problem list of current contest + beforeSend: csrfTokenHeader, + url: "/api/admin/contest_problem/?contest_id=" + vm.contestList[contestId-1].id, + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + vm.editProblemList = data.data; + } + else { + bsAlert(data.data); + } + } + }); + vm.editingContestId = 0; + vm.editingProblemContestIndex = contestId; + vm.editMode = vm.contestList[contestId-1].mode; + }, + addGroup: function() { + if (vm.group == -1) return; + if (vm.groupList[vm.group].id == 0){ + vm.passwordUsable = true; + vm.choseGroupList = []; + for (var i = 0; i < vm.groupList.length; i++) { + vm.groupList[i].chose = true; + } + } + vm.groupList[vm.group]. chose = true; + // index of the group is relative. It is related to user + vm.choseGroupList.push({name:vm.groupList[vm.group].name, index:vm.group, id:vm.groupList[vm.group].id}); + vm.group = -1; + }, + removeGroup: function(groupIndex){ + if (vm.groupList[vm.choseGroupList[groupIndex].index].id == 0){ + vm.passwordUsable = false; + for (var i = 0; i < vm.groupList.length; i++) { + vm.groupList[i].chose = false; + } + } + vm.groupList[vm.choseGroupList[groupIndex].index].chose = false; + vm.choseGroupList.remove(vm.choseGroupList[groupIndex]); + }, + add_problem: function () { + vm.$fire("up!showContestProblemPage", 0, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode); + }, + showProblemEditor: function(el) { + vm.$fire("up!showContestProblemPage", el.id, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode); + }, + getYesOrNo: function(yORn) { + if (yORn) return "是"; + return "否"; + } + }); + vm.$watch("showVisibleOnly", function() { + getPageData(1); + }) + } + getPageData(1); + + //init time picker + $("#contest_start_time").datetimepicker({ + format: "yyyy-mm-dd hh:ii", + minuteStep: 5, + weekStart: 1, + language: "zh-CN" + }); + $("#contest_end_time").datetimepicker({ + format: "yyyy-mm-dd hh:ii", + minuteStep: 5, + weekStart: 1, + language: "zh-CN" + }); + + function getPageData(page) { + var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=10"; + if (vm.showVisibleOnly) + url += "&visible=true" + if (vm.keyword != "") + url += "&keyword=" + vm.keyword; + $.ajax({ + url: url, + dataType: "json", + method: "get", + success: function (data) { + if (!data.code) { + vm.contestList = data.data.results; + vm.totalPage = data.data.total_page; + vm.previousPage = data.data.previous_page; + vm.nextPage = data.data.next_page; + vm.page = page; + } + else { + bsAlert(data.data); + } + } + }); + } + // Get group list + $.ajax({ // Get current user type + url: "/api/user/", + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + if (data.data.admin_type == 2) { // Is super user + vm.isGlobal = true; + vm.groupList.push({id:0,name:"所有人",chose:false}); + } + $.ajax({ // Get the group list of current user + beforeSend: csrfTokenHeader, + url: "/api/admin/group/", + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + if (!data.data.length) { + //this user have no group can use + return; + } + for (var i = 0; i < data.data.length; i++) { + var item = data.data[i]; + item["chose"] = false; + vm.groupList.push(item); + } + } + else { + bsAlert(data.data); + } + } + }); + } + } + }); + + }); + avalon.scan(); +}); diff --git a/static/src/js/app/admin/contest/contest_list.js b/static/src/js/app/admin/contest/contest_list.js deleted file mode 100644 index 1b2de7a..0000000 --- a/static/src/js/app/admin/contest/contest_list.js +++ /dev/null @@ -1,139 +0,0 @@ -require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker"], function ($, avalon, csrfTokenHeader, bsAlert, editor) { - - avalon.ready(function () { - if (avalon.vmodels.contestList) { - vm = avalon.vmodels.contestList; - vm.editingContest = 0; - } - else { - var vm = avalon.define({ - $id: "contestList", - contestList: [], - previousPage: 0, - nextPage: 0, - page: 1, - totalPage: 1, - group: "-1", - groupList: [], - keyword: "", - editingContestId: 0, - editTitle: "", - editProblemList: [], - editPassword: "", - editStartTime: "", - editEndTime: "", - editMode: "", - editShowRank: false, - editShowSubmission: false, - editProblemList: [], - editingProblemId: 0, - editSamples: [], - editTestCaseList: [], - editChoseGroupList: [], - modelNameList: ["ACM", "AC总数", "分数"], - contestTypeNameList: ["小组赛", "公开赛", "有密码保护的公开赛"], - getNext: function () { - if (!vm.nextPage) - return; - getPageData(vm.page + 1); - }, - getPrevious: function () { - if (!vm.previousPage) - return; - getPageData(vm.page - 1); - }, - getBtnClass: function (btn) { - if (btn == "next") { - return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; - } - else { - return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; - } - }, - getPage: function (page_index) { - getPageData(page_index); - }, - showEditContestArea: function (contestId) { - if (contestId == vm.editingContestId) - vm.editingContestId = 0; - else { - vm.editingContestId = contestId; - vm.editTitle = vm.contestList[contestId - 1].title; - vm.editEndTime = vm.contestList[contestId - 1].end_time; - vm.editPassword = vm.contestList[contestId - 1].password; - vm.editStartTime = vm.contestList[contestId - 1].start_time; - vm.editMode = vm.contestList[contestId - 1].mode; - vm.editChoseGroupList = []; - //= vm.contestList[contestId-1].group;// - /*for (var key in vm.contestList[contestId-1].groups){ - var id = parseInt(vm.contestList[contestId-1].groups); - for () - vm.editChoseGroupList.push({ - name:vm.groupList[vm.group].name, - index:index, - id:parseInt(vm.contestList[contestId-1].groups) - }); - }*/ - vm.editShowRank = vm.contestList[contestId - 1].show_rank; - vm.editShowSubmission = vm.contestList[contestId - 1].show_user_submission; - //vm.editProblemList = vm.contestList[contestId-1].problems - editor("#editor").setValue(vm.contestList[contestId - 1].description); - vm.editingProblemList = vm.contestList[contestId - 1].problemList; - } - } - }); - - getPageData(1); - } - - function getPageData(page) { - - var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=10"; - if (vm.keyword != "") - url += "&keyword=" + vm.keyword; - $.ajax({ - url: url, - dataType: "json", - method: "get", - success: function (data) { - if (!data.code) { - vm.contestList = data.data.results; - vm.totalPage = data.data.total_page; - vm.previousPage = data.data.previous_page; - vm.nextPage = data.data.next_page; - vm.page = page; - } - else { - bsAlert(data.data); - } - } - }); - } - - var isSuperAdmin = true; - - $.ajax({ //用于获取该用户创建的所有小组的ajax请求 - - url: "/api/admin/group/", - method: "get", - dataType: "json", - success: function (data) { - if (!data.code) { - if (!data.data.length) { - bsAlert("您的用户权限只能创建组内比赛,但是您还没有创建过小组"); - return; - } - for (var i = 0; i < data.data.length; i++) { - var item = data.data[i]; - item["chose"] = false; - vm.groupList.push(item); - } - } - else { - bsAlert(data.data); - } - } - }); - }); - avalon.scan(); -}); diff --git a/static/src/js/app/admin/contest/editProblem.js b/static/src/js/app/admin/contest/editProblem.js new file mode 100644 index 0000000..51a668d --- /dev/null +++ b/static/src/js/app/admin/contest/editProblem.js @@ -0,0 +1,203 @@ +require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI"], + function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { + + avalon.ready(function () { + + $("#edit-problem-form").validator() + .on('submit', function (e) { + if (!e.isDefaultPrevented()){ + e.preventDefault(); + if (vm.testCaseId == "") { + bsAlert("你还没有上传测试数据!"); + return false; + } + if (vm.description == "") { + bsAlert("题目描述不能为空!"); + return false; + } + if (vm.timeLimit < 1000 || vm.timeLimit > 5000) { + bsAlert("保证时间限制是一个1000-5000的合法整数"); + return false; + } + if (vm.samples.length == 0) { + bsAlert("请至少添加一组样例!"); + return false; + } + for (var i = 0; i < vm.samples.length; i++) { + if (vm.samples[i].input == "" || vm.samples[i].output == "") { + bsAlert("样例输入与样例输出不能为空!"); + return false; + } + } + var ajaxData = { + title: vm.title, + description: vm.description, + time_limit: vm.timeLimit, + memory_limit: vm.memoryLimit, + samples: [], + test_case_id: vm.testCaseId, + hint: vm.hint, + visible: vm.visible, + contest_id: avalon.vmodels.admin.$contestId, + input_description: vm.inputDescription, + output_description: vm.outputDescription, + sort_index: vm.sortIndex, + }; + if (vm.contestMode == '2') { + if (!vm.score) { + bsAlert("请输入有效的分值!") + return false; + } + ajaxData.score = vm.score; + } + var method = "post"; + if (avalon.vmodels.admin.$problemId) { + method = "put"; + ajaxData.id = avalon.vmodels.admin.$problemId; + } + + for (var i = 0; i < vm.samples.$model.length; i++) { + ajaxData.samples.push({input: vm.samples.$model[i].input, output: vm.samples.$model[i].output}); + } + + $.ajax({ + beforeSend: csrfTokenHeader, + url: "/api/admin/contest_problem/", + dataType: "json", + data: JSON.stringify(ajaxData), + method: method, + contentType: "application/json", + success: function (data) { + if (!data.code) { + bsAlert("题目编辑成功!"); + vm.goBack(true); + } + else { + bsAlert(data.data); + } + } + + }); + return false; + } + }); + + if (!avalon.vmodels.editProblem) + var vm = avalon.define({ + $id: "editProblem", + title: "", + description: "", + timeLimit: 0, + memoryLimit: 0, + samples: [], + hint: "", + sortIndex: "", + visible: true, + inputDescription: "", + outputDescription: "", + testCaseIdd: "", + contestMode: 0, + score: 1, + uploadSuccess: false, + testCaseList: [], + addSample: function () { + vm.samples.push({input: "", output: "", "visible": true}); + }, + delSample: function (sample) { + if (confirm("你确定要删除么?")) { + vm.samples.remove(sample); + } + }, + toggleSample: function (sample) { + sample.visible = !sample.visible; + }, + getBtnContent: function (item) { + if (item.visible) + return "折叠"; + return "展开"; + }, + goBack: function(check){ + if (check||confirm("这将丢失所有的改动,确定要继续么?")) { + vm.$fire("up!showContestListPage"); + } + } + }); + else + vm = avalon.vmodels.editProblem; + + var hintEditor = editor("#hint"); + var descriptionEditor = editor("#problemDescription"); + var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) { + if (response.code) + bsAlert(response.data); + else { + vm.testCaseId = response.data.test_case_id; + vm.uploadSuccess = true; + vm.testCaseList = []; + for (var i = 0; i < response.data.file_list.input.length; i++) { + vm.testCaseList.push({ + input: response.data.file_list.input[i], + output: response.data.file_list.output[i] + }); + } + bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据"); + } + }); + + vm.contestMode = avalon.vmodels.admin.$contestMode; + if (avalon.vmodels.admin.$problemId){ + $.ajax({ + url: "/api/admin/contest_problem/?contest_problem_id=" + avalon.vmodels.admin.$problemId, + method: "get", + dataType: "json", + success: function (data) { + if (data.code) { + bsAlert(data.data); + } + else { // Edit mode load the problem data + var problem = data.data; + vm.testCaseList = []; + vm.sortIndex = problem.sort_index; + vm.title = problem.title; + vm.description = problem.description; + vm.timeLimit = problem.time_limit; + vm.memoryLimit = problem.memory_limit; + vm.hint = problem.hint; + vm.visible = problem.visible; + vm.inputDescription = problem.input_description; + vm.outputDescription = problem.output_description; + vm.score = problem.score; + vm.samples = []; + vm.testCaseId = problem.test_case_id; + for (var i = 0; i < problem.samples.length; i++) { + vm.samples.push({ + input: problem.samples[i].input, + output: problem.samples[i].output, + visible: false + }) + } + hintEditor.setValue(vm.hint); + descriptionEditor.setValue(vm.description); + } + } + }); + } + else { //Create new problem Set default values + vm.testCaseList = []; + vm.title = ""; + vm.timeLimit = 1000; + vm.memoryLimit = 256; + vm.samples = []; + vm.visible = true; + vm.inputDescription = ""; + vm.outputDescription = ""; + vm.testCaseId = ""; + vm.sortIndex = ""; + vm.score = 0; + hintEditor.setValue(""); + descriptionEditor.setValue(""); + } + }); + avalon.scan(); + + }); diff --git a/static/src/js/app/admin/problem/add_problem.js b/static/src/js/app/admin/problem/addProblem.js similarity index 71% rename from static/src/js/app/admin/problem/add_problem.js rename to static/src/js/app/admin/problem/addProblem.js index 7331a8a..d34cd9a 100644 --- a/static/src/js/app/admin/problem/add_problem.js +++ b/static/src/js/app/admin/problem/addProblem.js @@ -1,7 +1,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI"], function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { avalon.ready(function () { - avalon.vmodels.addProblem = null; + $("#add-problem-form").validator() .on('submit', function (e) { if (!e.isDefaultPrevented()){ @@ -63,6 +63,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE success: function (data) { if (!data.code) { bsAlert("题目添加成功!"); + location.hash = "problem/problem_list"; } else { bsAlert(data.data); @@ -92,41 +93,61 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE var hintEditor = editor("#hint"); var problemDescription = editor("#problemDescription"); - - var vm = avalon.define({ - $id: "addProblem", - title: "", - description: "", - timeLimit: 1000, - memoryLimit: 256, - samples: [{input: "", output: "", "visible": true}], - hint: "", - visible: true, - difficulty: 0, - tags: [], - inputDescription: "", - outputDescription: "", - testCaseId: "", - testCaseList: [], - uploadSuccess: false, - source: "", - addSample: function () { - vm.samples.push({input: "", output: "", "visible": true}); - }, - delSample: function (sample) { - if (confirm("你确定要删除么?")) { - vm.samples.remove(sample); + if (avalon.vmodels.addProblem) { + var vm = avalon.vmodels.addProblem; + vm.title = ""; + vm.description = ""; + vm.timeLimit = 1000; + vm.memoryLimit = 256; + vm.samples = [{input: "", output: "", "visible": true}]; + vm.hint = ""; + vm.visible = true; + vm.difficulty = 0; + vm.tags = []; + vm.inputDescription = ""; + vm.outputDescription = ""; + vm.testCaseId = ""; + vm.testCaseList = []; + vm.uploadSuccess = false; + vm.source = ""; + hintEditor.setValue(""); + problemDescription.setValue(""); + } + else + var vm = avalon.define({ + $id: "addProblem", + title: "", + description: "", + timeLimit: 1000, + memoryLimit: 256, + samples: [{input: "", output: "", "visible": true}], + hint: "", + visible: true, + difficulty: 0, + tags: [], + inputDescription: "", + outputDescription: "", + testCaseId: "", + testCaseList: [], + uploadSuccess: false, + source: "", + addSample: function () { + vm.samples.push({input: "", output: "", "visible": true}); + }, + delSample: function (sample) { + if (confirm("你确定要删除么?")) { + vm.samples.remove(sample); + } + }, + toggleSample: function (sample) { + sample.visible = !sample.visible; + }, + getBtnContent: function (item) { + if (item.visible) + return "折叠"; + return "展开"; } - }, - toggleSample: function (sample) { - sample.visible = !sample.visible; - }, - getBtnContent: function (item) { - if (item.visible) - return "折叠"; - return "展开"; - } - }); + }); var tagAutoCompleteList = []; diff --git a/static/src/js/app/admin/problem/edit_problem.js b/static/src/js/app/admin/problem/editProblem.js similarity index 80% rename from static/src/js/app/admin/problem/edit_problem.js rename to static/src/js/app/admin/problem/editProblem.js index 72a6b7c..a746d7a 100644 --- a/static/src/js/app/admin/problem/edit_problem.js +++ b/static/src/js/app/admin/problem/editProblem.js @@ -3,8 +3,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE avalon.ready(function () { - - $("#edit-problem-form").validator() .on('submit', function (e) { if (!e.isDefaultPrevented()){ @@ -66,6 +64,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE success: function (data) { if (!data.code) { bsAlert("题目编辑成功!"); + vm.showProblemListPage(); } else { bsAlert(data.data); @@ -76,49 +75,60 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE return false; } }); - - if(avalon.vmodels.editProblem){ - var vm = avalon.vmodels.editProblem; - } - else { - var vm = avalon.define({ - $id: "editProblem", - title: "", - description: "", - timeLimit: -1, - memoryLimit: -1, - samples: [], - hint: "", - visible: true, - difficulty: 0, - inputDescription: "", - outputDescription: "", - testCaseIdd: "", - uploadSuccess: false, - source: "", - testCaseList: [], - addSample: function () { - vm.samples.push({input: "", output: "", "visible": true}); - }, - delSample: function (sample) { - if (confirm("你确定要删除么?")) { - vm.samples.remove(sample); - } - }, - toggleSample: function (sample) { - sample.visible = !sample.visible; - }, - getBtnContent: function (item) { - if (item.visible) - return "折叠"; - return "展开"; - }, - showProblemListPage: function () { - vm.$fire("up!showProblemListPage"); + if (avalon.vmodels.editProblem) { + var vm = avalon.vmodels.editProblem; + title: "", + description= ""; + timeLimit= -1; + memoryLimit= -1; + samples= []; + hint= ""; + visible= true; + difficulty= 0; + inputDescription= ""; + outputDescription= ""; + testCaseIdd= ""; + uploadSuccess= false; + source= ""; + testCaseList= []; + } + else + var vm = avalon.define({ + $id: "editProblem", + title: "", + description: "", + timeLimit: -1, + memoryLimit: -1, + samples: [], + hint: "", + visible: true, + difficulty: 0, + inputDescription: "", + outputDescription: "", + testCaseIdd: "", + uploadSuccess: false, + source: "", + testCaseList: [], + addSample: function () { + vm.samples.push({input: "", output: "", "visible": true}); + }, + delSample: function (sample) { + if (confirm("你确定要删除么?")) { + vm.samples.remove(sample); } - }); - - } + }, + toggleSample: function (sample) { + sample.visible = !sample.visible; + }, + getBtnContent: function (item) { + if (item.visible) + return "折叠"; + return "展开"; + }, + showProblemListPage: function(){ + vm.$fire("up!showProblemListPage"); + } + }); var hintEditor = editor("#hint"); var descriptionEditor = editor("#problemDescription"); var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) { @@ -148,7 +158,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE } else { var problem = data.data; - console.log(problem); vm.title = problem.title; vm.description = problem.description; vm.timeLimit = problem.time_limit; @@ -204,4 +213,4 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE }); avalon.scan(); - }); + }); \ No newline at end of file diff --git a/static/src/js/app/admin/problem/problem.js b/static/src/js/app/admin/problem/problem.js index cb1a0f0..2632ebf 100644 --- a/static/src/js/app/admin/problem/problem.js +++ b/static/src/js/app/admin/problem/problem.js @@ -41,10 +41,8 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT vm.$fire("up!showProblemSubmissionPage", problemId); } }); - - getPageData(1); } - + getPageData(1); function getPageData(page) { var url = "/api/admin/problem/?paging=true&page=" + page + "&page_size=10"; if (vm.keyword != "") diff --git a/static/src/js/app/admin/problem/submission_list.js b/static/src/js/app/admin/problem/submissionList.js similarity index 91% rename from static/src/js/app/admin/problem/submission_list.js rename to static/src/js/app/admin/problem/submissionList.js index 8593557..405cffa 100644 --- a/static/src/js/app/admin/problem/submission_list.js +++ b/static/src/js/app/admin/problem/submissionList.js @@ -40,6 +40,12 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT }, getPage: function (page_index) { getPageData(page_index); + }, + showSubmissionDetailPage: function (submissionId) { + + }, + showProblemListPage: function(){ + vm.$fire("up!showProblemListPage"); } }); @@ -66,9 +72,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT }); } - avalon.scan(); - }); - + avalon.scan(); }); \ No newline at end of file diff --git a/static/src/js/app/admin/user/userList.js b/static/src/js/app/admin/user/userList.js index 0842dc5..3f80c89 100644 --- a/static/src/js/app/admin/user/userList.js +++ b/static/src/js/app/admin/user/userList.js @@ -3,58 +3,68 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($, // avalon:定义模式 userList avalon.ready(function () { - avalon.vmodels.userList = null; - var vm = avalon.define({ - $id: "userList", - //通用变量 - userList: [], - previousPage: 0, - nextPage: 0, - page: 1, - editingUserId: 0, - totalPage: 1, - userType: ["一般用户", "管理员", "超级管理员"], - keyword: "", - showAdminOnly: false, - //编辑区域同步变量 - username: "", - realName: "", - email: "", - adminType: 0, - id: 0, - getNext: function () { - if (!vm.nextPage) - return; - getPageData(vm.page + 1); - }, - getPrevious: function () { - if (!vm.previousPage) - return; - getPageData(vm.page - 1); - }, - getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑 - if (btn) { - return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; + //avalon.vmodels.userList = null; + if (avalon.vmodels.userList) { + var vm = avalon.vmodels.userList; + // initialize avalon object + userList = []; previousPage= 0; nextPage= 0; page = 1; + editingUserId= 0; totalPage = 1; keyword= ""; showAdminOnly= false; + //user editor fields + username= ""; realName= ""; email= ""; adminType= 0; id= 0; + } + else { + var vm = avalon.define({ + $id: "userList", + //通用变量 + userList: [], + previousPage: 0, + nextPage: 0, + page: 1, + editingUserId: 0, + totalPage: 1, + userType: ["一般用户", "管理员", "超级管理员"], + keyword: "", + showAdminOnly: false, + //编辑区域同步变量 + username: "", + realName: "", + email: "", + adminType: 0, + id: 0, + getNext: function () { + if (!vm.nextPage) + return; + getPageData(vm.page + 1); + }, + getPrevious: function () { + if (!vm.previousPage) + return; + getPageData(vm.page - 1); + }, + getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑 + if (btn == "next") { + return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; + } + else { + return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; + } + }, + editUser: function (user) { //点击编辑按钮的事件,显示/隐藏编辑区 + vm.username = user.username; + vm.realName = user.real_name; + vm.adminType = user.admin_type; + vm.email = user.email; + vm.id = user.id; + if (vm.editingUserId == user.id) + vm.editingUserId = 0; + else + vm.editingUserId = user.id; + }, + search: function () { + getPageData(1); } - else { - return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; - } - }, - editUser: function (user) { //点击编辑按钮的事件,显示/隐藏编辑区 - vm.username = user.username; - vm.realName = user.real_name; - vm.adminType = user.admin_type; - vm.email = user.email; - vm.id = user.id; - if (vm.editingUserId == user.id) - vm.editingUserId = 0; - else - vm.editingUserId = user.id; - }, - search: function () { - getPageData(1); - } - }); + }); + } vm.$watch("showAdminOnly", function () { getPageData(1); }); diff --git a/submission/views.py b/submission/views.py index af2c41d..17c1615 100644 --- a/submission/views.py +++ b/submission/views.py @@ -156,5 +156,4 @@ def my_submission_list_page(request, page=1): return render(request, "oj/submission/my_submissions_list.html", {"submissions": current_page, "page": int(page), - "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20}) - + "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20}) \ No newline at end of file diff --git a/template/admin/contest/add_contest.html b/template/admin/contest/add_contest.html index 44ae98a..6f237c5 100644 --- a/template/admin/contest/add_contest.html +++ b/template/admin/contest/add_contest.html @@ -6,8 +6,9 @@
- + +
@@ -17,51 +18,46 @@
+
请填写比赛描述
-
-
- -
-
+ ms-duplex="startTime" data-error="请填写比赛开始时间" required> +
+
-
-
-
-
- -
-
- -
-
-
- + ms-duplex="endTime" data-error="请填写比赛结束时间" required> + +
+
- + +
+
+
+ +
+
-
{{el.name}}
+
{{el.name}}
@@ -94,135 +90,12 @@
-
- - 添加 - - - - - - - - - - - - - -
编号题目测试数据
题目{{ $index+1 }}{{ el.title }}{{ el.testCaseList.length }}组 - 编辑 - 删除 -
- -
-
- -
-
-
- 题目{{editingProblemId}} - 隐藏 - 删除 -
-
-
- - -
-
-
- - - 请填写题目描述 -
-
- - -
-
- - -
-
-
- - -
-
-
- - -
-
-
- - 添加 -
-
-
- 样例{{$index + 1}} - {{ getBtnContent(sample)}} - 删除 -
-
-
-
- - -
-
-
-
- - -
-
-
-
-
-
-
- - - - - - - - - - - - - -
编号输入文件名输出文件名
{{$index}}{{ el.input }}{{ el.output }}
-
-
-
-
-
-
- -
-
选择文件
-
- 请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:
- 1.in 1.out 2.in 2.out -

-
@@ -230,4 +103,4 @@ - + diff --git a/template/admin/contest/contest_list.html b/template/admin/contest/contest_list.html index ab878ae..5c15629 100644 --- a/template/admin/contest/contest_list.html +++ b/template/admin/contest/contest_list.html @@ -15,154 +15,174 @@ 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")}} + {{ getYesOrNo(el.show_rank) }} + {{ getYesOrNo(el.visible) }} {{ 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 }}组 - 编辑 - 删除 -
+
+
+ + +
+ 请填写比赛描述 +
+
+
+ + +
+ + +
+
+
+
+ + +
+ + +
+
+
+
+ + +
+ +
+
+
+ + +
+
+
{{el.name}}
+
+
+ +
+
+ +
+
+ +
+
+ + +
+ + + +
+
+
+
+ +
+
+ + +
+ +
+
+
+ + +
+ +
+
+
+ +
+ +
+
+ + 添加 + + + + + + + + + + + + + + + + + +
编号题目分值可见创建时间
{{ el.sort_index }}{{ el.title }}{{ el.score}}{{ getYesOrNo(el.visible) }}{{ el.create_time|date("yyyy-MM-dd HH:mm:ss") }} + 编辑 +
-
+
- + diff --git a/template/admin/contest/edit_problem.html b/template/admin/contest/edit_problem.html new file mode 100644 index 0000000..331917c --- /dev/null +++ b/template/admin/contest/edit_problem.html @@ -0,0 +1,138 @@ +
+
+ + +
+ + +
+
+
+ + +
+
+ +
+ + + 请填写题目描述 +
+ + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+

+ + 添加 + +
+
+ +
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+

+
+ 请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:
+ 1.in 1.out 2.in 2.out +
+ + + + + + + + + + + +
编号输入文件名输出文件名
{{ $index + 1 }}{{ el.input }}{{ el.output }}
+
+
+
+
选择文件
+
+
+
+ + +
+
+ +
+
+
+ + diff --git a/template/admin/problem/add_problem.html b/template/admin/problem/add_problem.html index fadea4c..38c22c6 100644 --- a/template/admin/problem/add_problem.html +++ b/template/admin/problem/add_problem.html @@ -134,4 +134,4 @@
- \ No newline at end of file + \ No newline at end of file diff --git a/template/admin/problem/edit_problem.html b/template/admin/problem/edit_problem.html index 3dd0870..0a2b7b5 100644 --- a/template/admin/problem/edit_problem.html +++ b/template/admin/problem/edit_problem.html @@ -140,4 +140,4 @@ - + diff --git a/template/admin/problem/submission_list.html b/template/admin/problem/submission_list.html index 6d94dab..70aacb8 100644 --- a/template/admin/problem/submission_list.html +++ b/template/admin/problem/submission_list.html @@ -1,4 +1,10 @@
+

提交列表

@@ -25,4 +31,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/template/oj/contest/contest_rank.html b/template/oj/contest/contest_rank.html new file mode 100644 index 0000000..89d1e40 --- /dev/null +++ b/template/oj/contest/contest_rank.html @@ -0,0 +1,30 @@ +{% extends "oj_base.html" %} +{% block body %} + {% load contest %} +
+
+ +
+
+
+
+
+
+{% endblock %} + + +{% block js_block %} +{% endblock %} \ No newline at end of file