diff --git a/.gitignore b/.gitignore index 285cfd82..1fbe8ef2 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ log/ static/release/css static/release/js static/release/img +static/src/upload_image/* build.txt tmp/ test_case/ \ No newline at end of file diff --git a/Accessories/__init__.py b/Accessories/__init__.py new file mode 100644 index 00000000..3ed9fd0f --- /dev/null +++ b/Accessories/__init__.py @@ -0,0 +1 @@ +__author__ = 'root' diff --git a/Accessories/reJudge.py b/Accessories/reJudge.py new file mode 100644 index 00000000..dc1ad2e2 --- /dev/null +++ b/Accessories/reJudge.py @@ -0,0 +1,74 @@ +import django +from contest.models import * +from problem.models import * +from submission.models import Submission + +import redis + +from judge.judger_controller.tasks import judge +from judge.judger_controller.settings import redis_config + +django.setup() + + +def rejudge(submission): + # for submission in submission: + # submission_id = submission.id + # try: + # command = "%s run -t -i --privileged --rm=true " \ + # "-v %s:/var/judger/test_case/ " \ + # "-v %s:/var/judger/code/ " \ + # "%s " \ + # "python judge/judger/run.py " \ + # "--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % \ + # (docker_config["docker_path"], + # test_case_dir, + # source_code_dir, + # docker_config["image_name"], + # submission_id, str(time_limit), str(memory_limit), test_case_id) + # subprocess.call(command, shell=docker_config["shell"]) + # except Exception as e: + # print e + return + + +def easy_rejudge(submissions, map_table, user_id, contest_id=None): + try: + user = User.objects.get(pk=user_id) + except User.DoesNotExist: + print "User.DoesNotExist!" + return + problemDict = {} + for oldSubmission in submission: + problem_id = map_table[oldSubmission.problem_id] + + if problem_id in problemDict: + problem = problemDict[problem_id] + else: + try: + p = Problem.objects.get(pk=problem_id) + except Problem.DoesNotExist: + print " Problem.DoesNotExist!" + str(problem_id) + continue + problem = p + problemDict[problem_id] = p + + submission = Submission.objects.create( + user_id=user_id, + language=oldSubmission.language, + code=oldSubmission.code, + contest_id=contest_id, + problem_id=problem_id, + originResult=oldSubmission.result + ) + try: + judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id) + except Exception: + print "error!" + continue + + r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"]) + r.incr("judge_queue_length") + + return + diff --git a/Accessories/utils.py b/Accessories/utils.py new file mode 100755 index 00000000..17ab0963 --- /dev/null +++ b/Accessories/utils.py @@ -0,0 +1,61 @@ +import django +from contest.models import * +from problem.models import * +django.setup() +def add_exist_problem_to_contest(problems, contest_id): + try: + contest = Contest.objects.get(pk=contest_id) + except Contest.DoesNotExist: + print "Contest Doesn't Exist!" + return + i = 1 + for problem in problems: + print "Add the problem:" + print problem.title + print "The sort Index is" + str(i) + " You Can modify it latter as you like~" + ContestProblem.objects.create(contest=contest, sort_index=str(i), + title=problem.title, description=problem.description, + input_description=problem.input_description, + output_description=problem.output_description, + samples=problem.samples, + test_case_id=problem.test_case_id, + hint=problem.hint, + created_by=problem.created_by, + time_limit=problem.time_limit, + memory_limit=problem.memory_limit) + i += 1 + return +def add_contest_problem_to_problem(contest_id): + try: + contest = Contest.objects.get(pk=contest_id) + except Contest.DoesNotExist: + print "Contest Doesn't Exist!" + return + #Get all problems in this contest + problems = ContestProblem.objects.filter(contest=contest) + + #get a tag + try: + tag = ProblemTag.objects.get(name=contest.title) + except ProblemTag.DoesNotExist: + tag = ProblemTag.objects.create(name=contest.title) + + #for each problem + for problem in problems: + print "Add problem to problem list:" + print problem.title + p = Problem.objects.create(title=problem.title, + description=problem.description, + input_description=problem.input_description, + output_description=problem.output_description, + samples=problem.samples, + test_case_id=problem.test_case_id, + hint=problem.hint, + created_by=problem.created_by, + time_limit=problem.time_limit, + memory_limit=problem.memory_limit, + visible = False, + difficulty = 0, + source = contest.title) + p.tags.add(tag) + return \ No newline at end of file diff --git a/contest/migrations/0008_auto_20150912_1912.py b/contest/migrations/0008_auto_20150912_1912.py new file mode 100644 index 00000000..ee2a8f50 --- /dev/null +++ b/contest/migrations/0008_auto_20150912_1912.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', '0007_contestsubmission_ac_time'), + ] + + operations = [ + migrations.RenameField( + model_name='contest', + old_name='show_rank', + new_name='real_time_rank', + ), + ] diff --git a/contest/models.py b/contest/models.py index a76913be..258ee19c 100644 --- a/contest/models.py +++ b/contest/models.py @@ -17,8 +17,8 @@ class Contest(models.Model): description = models.TextField() # 比赛模式:0 即为是acm模式,1 即为是按照总的 ac 题目数量排名模式 mode = models.IntegerField() - # 是否显示排名结果 - show_rank = models.BooleanField() + # 是否显示实时排名结果 + real_time_rank = models.BooleanField() # 是否显示别人的提交记录 show_user_submission = models.BooleanField() # 只能超级管理员创建公开赛,管理员只能创建小组内部的比赛 diff --git a/contest/views.py b/contest/views.py index f5ccffb8..ad5ccf76 100644 --- a/contest/views.py +++ b/contest/views.py @@ -353,8 +353,8 @@ def contest_list_page(request, page=1): # 筛选我能参加的比赛 join = request.GET.get("join", None) - if join: - contests = contests.filter(Q(contest_type__in=[PUBLIC_CONTEST, PASSWORD_PUBLIC_CONTEST]) | Q(groups__in=request.user.group_set.all())). \ + if request.user.is_authenticated and join: + contests = contests.filter(Q(contest_type__in=[1, 2]) | Q(groups__in=request.user.group_set.all())). \ filter(end_time__gt=datetime.datetime.now(), start_time__lt=datetime.datetime.now()) paginator = Paginator(contests, 20) diff --git a/contest_submission/views.py b/contest_submission/views.py index f136245f..765d116e 100644 --- a/contest_submission/views.py +++ b/contest_submission/views.py @@ -119,6 +119,14 @@ def contest_problem_submissions_list_page(request, contest_id, page=1): next_page = current_page.next_page_number() except Exception: pass + + # 如果该用户是超级管理员那么他可以查看所有的提交记录详情 + if request.user.admin_type > 1: + return render(request, "oj/contest/submissions_list_admin.html", + {"submissions": current_page, "page": int(page), + "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20, + "contest": contest}) + return render(request, "oj/contest/submissions_list.html", {"submissions": current_page, "page": int(page), "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20, @@ -155,4 +163,4 @@ class ContestSubmissionAdminAPIView(APIView): return error_response(u"参数错误!") if problem_id: submissions = submissions.filter(problem_id=problem_id) - return paginate(request, submissions, SubmissionSerializer) \ No newline at end of file + return paginate(request, submissions, SubmissionSerializer) diff --git a/oj/local_settings.py b/oj/local_settings.py index b9232918..51e9095e 100644 --- a/oj/local_settings.py +++ b/oj/local_settings.py @@ -25,9 +25,17 @@ DATABASES = { } } +REDIS_CACHE = { + "host": "121.42.32.129", + "port": 6379, + "db": 1 +} + DEBUG = True # 同理 这是 web 服务器的上传路径 -TEST_CASE_DIR = os.path.join(BASE_DIR, 'test_case/') +TEST_CASE_DIR = os.path.join(BASE_DIR, 'test_case/') ALLOWED_HOSTS = [] + +IMAGE_UPLOAD_DIR = os.path.join(BASE_DIR, 'static/src/upload_image/') \ No newline at end of file diff --git a/oj/server_settings.py b/oj/server_settings.py index 185bf6bb..cc5ee829 100644 --- a/oj/server_settings.py +++ b/oj/server_settings.py @@ -29,9 +29,17 @@ DATABASES = { } } +REDIS_CACHE = { + "host": "127.0.0.1", + "port": 6379, + "db": 1 +} + DEBUG = True # 同理 这是 web 服务器的上传路径 TEST_CASE_DIR = '/root/test_case/' ALLOWED_HOSTS = ['*'] + +IMAGE_UPLOAD_DIR = '/var/mnt/source/OnlineJudge/static/src/upload_image/' diff --git a/oj/urls.py b/oj/urls.py index 8d6e819a..e9cc880b 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -19,6 +19,7 @@ from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, Problem from submission.views import SubmissionAPIView, SubmissionAdminAPIView, SubmissionShareAPIView from contest_submission.views import ContestSubmissionAPIView, ContestSubmissionAdminAPIView from monitor.views import QueueLengthMonitorAPIView +from utils.views import SimditorImageUploadAPIView from contest_submission.views import contest_problem_my_submissions_list_page @@ -53,6 +54,7 @@ urlpatterns = [ url(r'^api/submission/$', SubmissionAPIView.as_view(), name="submission_api"), url(r'^api/group_join/$', JoinGroupAPIView.as_view(), name="group_join_api"), + url(r'^api/admin/upload_image/$', SimditorImageUploadAPIView.as_view(), name="simditor_upload_image"), url(r'^api/admin/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_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"), @@ -112,5 +114,6 @@ urlpatterns = [ url(r'^help/$', TemplateView.as_view(template_name="utils/help.html"), name="help_page"), url(r'^api/submission/share/$', SubmissionShareAPIView.as_view(), name="submission_share_api"), + url(r'^captcha/$', "utils.captcha.views.show_captcha", name="show_captcha"), ] diff --git a/problem/views.py b/problem/views.py index 1098ac16..c8077329 100644 --- a/problem/views.py +++ b/problem/views.py @@ -147,9 +147,12 @@ class TestCaseUploadAPIView(APIView): f = request.FILES["file"] tmp_zip = "/tmp/" + rand_str() + ".zip" - with open(tmp_zip, "wb") as test_case_zip: - for chunk in f: - test_case_zip.write(chunk) + try: + with open(tmp_zip, "wb") as test_case_zip: + for chunk in f: + test_case_zip.write(chunk) + except IOError: + return error_response(u"上传错误,写入临时目录失败") test_case_file = zipfile.ZipFile(tmp_zip, 'r') name_list = test_case_file.namelist() @@ -254,7 +257,7 @@ def problem_list_page(request, page=1): tag = ProblemTag.objects.get(name=tag_text) except ProblemTag.DoesNotExist: return error_page(request, u"标签不存在") - problems = tag.problem_set.all() + problems = tag.problem_set.all().filter(visible=True) paginator = Paginator(problems, 20) try: diff --git a/static/release/fis-conf.js b/static/release/fis-conf.js index ce128cdd..f0935c30 100644 --- a/static/release/fis-conf.js +++ b/static/release/fis-conf.js @@ -1,3 +1,13 @@ /** * Created by virusdefender on 8/25/15. */ + +fis.match('*.{js,css,png,gif}', { + useHash: true // 开启 md5 戳 +}); + +fis.config.set( +'roadmap.path', +[{reg:'*.html',isHtmlLike : true} +]) +; \ No newline at end of file diff --git a/static/src/js/app/admin/contest/addContest.js b/static/src/js/app/admin/contest/addContest.js index a87e4e29..8aa5354f 100644 --- a/static/src/js/app/admin/contest/addContest.js +++ b/static/src/js/app/admin/contest/addContest.js @@ -2,40 +2,46 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date "validator"], function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { - //avalon.vmodels.add_contest = null; $("#add-contest-form").validator().on('submit', function (e) { - if (!e.isDefaultPrevented()){ + if (!e.isDefaultPrevented()) { e.preventDefault(); var ajaxData = { title: vm.title, description: vm.description, mode: vm.mode, contest_type: 0, - show_rank: vm.showRank, + real_time_rank: vm.realTimeRank, show_user_submission: vm.showSubmission, start_time: vm.startTime, end_time: vm.endTime, visible: false }; - if (vm.choseGroupList.length == 0) { - bsAlert("你没有选择参赛用户!"); - return false; + + var selectedGroups = []; + if (!vm.isGlobal) { + for (var i = 0; i < vm.allGroups.length; i++) { + if (vm.allGroups[i].isSelected) { + selectedGroups.push(vm.allGroups[i].id); + } + } + ajaxData.groups = selectedGroups; } - if (vm.choseGroupList[0].id == 0) { //everyone | public contest + else { if (vm.password) { ajaxData.password = vm.password; ajaxData.contest_type = 2; } - else{ + 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)) + if (!vm.isGlobal && !selectedGroups.length) { + bsAlert("你没有选择参赛用户!"); + return false; + } + if (vm.editDescription == "") { + bsAlert("比赛描述不能为空!"); + return false; } - $.ajax({ // Add contest beforeSend: csrfTokenHeader, url: "/api/admin/contest/", @@ -45,20 +51,18 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date method: "post", 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"; + bsAlert("添加成功!将转到比赛列表页以便为比赛添加问题(注意比赛当前状态为:隐藏)"); + vm.title = ""; + vm.description = ""; + vm.startTime = ""; + vm.endTime = ""; + vm.password = ""; + vm.mode = "0"; + vm.showSubmission = true; + location.hash = "#contest/contest_list"; + vm.isGlobal = true; + vm.allGroups = []; + vm.showGlobalViewRadio = true; } else { bsAlert(data.data); @@ -70,80 +74,59 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date }); 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]); - } - }); + 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: "0", + showSubmission: true, + isGlobal: true, + allGroups: [], + showGlobalViewRadio: true, + realTimeRank: true + }); - $.ajax({ // Get current user type + $.ajax({ 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}); + var admin_type = data.data.admin_type; + if (data.data.admin_type == 1) { + vm.isGlobal = false; + vm.showGlobalViewRadio = 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) { - return; - } - for (var i = 0; i < data.data.length; i++) { - var item = data.data[i]; - item["chose"] = false; - vm.groupList.push(item); - } + } + $.ajax({ + url: "/api/admin/group/", + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + if (!data.data.length) { + if (admin_type != 2) + bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组"); + return; } - else { - bsAlert(data.data); + for (var i = 0; i < data.data.length; i++) { + var item = data.data[i]; + item["isSelected"] = false; + vm.allGroups.push(item); } } - }); - } + else { + bsAlert(data.data); + } + } + }); } }); diff --git a/static/src/js/app/admin/contest/contestList.js b/static/src/js/app/admin/contest/contestList.js index c920cd6c..315023ce 100644 --- a/static/src/js/app/admin/contest/contestList.js +++ b/static/src/js/app/admin/contest/contestList.js @@ -3,21 +3,39 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", avalon.ready(function () { $("#edit-contest-form").validator().on('submit', function (e) { - if (!e.isDefaultPrevented()){ + 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, + id: vm.contestList[vm.editingContestId - 1].id, + title: vm.editTitle, + description: vm.editDescription, + mode: vm.editMode, + contest_type: 0, + real_time_rank: vm.editRealTimeRank, show_user_submission: vm.editShowSubmission, - start_time: vm.editStartTime, - end_time: vm.editEndTime, - visible: vm.editVisible + start_time: vm.editStartTime, + end_time: vm.editEndTime, + visible: vm.editVisible }; - if (vm.choseGroupList.length == 0) { + + var selectedGroups = []; + if (!vm.isGlobal) { + for (var i = 0; i < vm.allGroups.length; i++) { + if (vm.allGroups[i].isSelected) { + selectedGroups.push(vm.allGroups[i].id); + } + } + ajaxData.groups = selectedGroups; + } + else { + if (vm.password) { + ajaxData.password = vm.editPassword; + ajaxData.contest_type = 2; + } + else + ajaxData.contest_type = 1; + } + if (!vm.isGlobal && !selectedGroups.length) { bsAlert("你没有选择参赛用户!"); return false; } @@ -25,22 +43,8 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", 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 + $.ajax({ // modify contest info beforeSend: csrfTokenHeader, url: "/api/admin/contest/", dataType: "json", @@ -52,7 +56,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", if (!data.code) { bsAlert("修改成功!"); vm.editingContestId = 0; // Hide the editor - vm.getPage(1); // Refresh the contest list + vm.getPage(1); // Refresh the contest list } else { bsAlert(data.data); @@ -63,138 +67,124 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", 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); - } + 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.keyword = ""; + vm.editingContestId = 0; + vm.editTitle = ""; + vm.editDescription = ""; + vm.editProblemList = []; + vm.editPassword = ""; + vm.editStartTime = ""; + vm.editEndTime = ""; + vm.editMode = ""; + vm.editShowSubmission = false; + vm.editVisible = false; + vm.editingProblemContestIndex = 0; + vm.editRealTimeRank = true; + } + else { + var vm = avalon.define({ + $id: "contestList", + contestList: [], + previousPage: 0, + nextPage: 0, + page: 1, + totalPage: 1, + showVisibleOnly: false, + keyword: "", + editingContestId: 0, + editTitle: "", + editDescription: "", + editProblemList: [], + editPassword: "", + editStartTime: "", + editEndTime: "", + editMode: "", + editShowSubmission: false, + editVisible: false, + editRealTimeRank: true, + editingProblemContestIndex: 0, + isGlobal: true, + allGroups: [], + showGlobalViewRadio: true, - 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; + 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; + vm.editRealTimeRank = vm.contestList[contestId - 1].real_time_rank; + if (vm.contestList[contestId - 1].contest_type == 0) { //contest type == 0, contest in group + vm.isGlobal = false; + for (var i = 0; i < vm.allGroups.length; i++) { + vm.allGroups[i].isSelected = false; + } + for (var i = 0; i < vm.contestList[contestId - 1].groups.length; i++) { + var id = parseInt(vm.contestList[contestId - 1].groups[i]); + + for (var index = 0; vm.allGroups[index]; index++) { + if (vm.allGroups[index].id == id) { + vm.allGroups[index].isSelected = true; + break; + } + } } - vm.groupList[index].chose = true; - vm.choseGroupList.push({ - name:vm.groupList[index].name, - index:index, - id:id - }); } + else { + vm.isGlobal = true; + } + vm.editShowSubmission = vm.contestList[contestId - 1].show_user_submission; + editor("#editor").setValue(vm.contestList[contestId - 1].description); + vm.editingProblemContestIndex = 0; } - else{ - vm.group = "0"; - vm.addGroup()//vm.editChoseGroupList = [0]; id 0 is for the group of everyone~ + }, + showEditProblemArea: function (contestId) { + if (vm.editingProblemContestIndex == contestId) { + vm.editingProblemContestIndex = 0; + return; } - 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 + 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, + url: "/api/admin/contest_problem/?contest_id=" + vm.contestList[contestId - 1].id, method: "get", dataType: "json", success: function (data) { @@ -206,51 +196,27 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", } } }); - 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]); + vm.editingContestId = 0; + vm.editingProblemContestIndex = contestId; + vm.editMode = vm.contestList[contestId - 1].mode; }, - addProblem: function () { - vm.$fire("up!showContestProblemPage", 0, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode); - }, - showProblemEditPage: function(el) { - vm.$fire("up!showContestProblemPage", el.id, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode); - }, - showSubmissionPage: function(el) { - var problemId = 0 - if (el) - problemId = el.id; - vm.$fire("up!showContestSubmissionPage", problemId, vm.contestList[vm.editingProblemContestIndex-1].id, vm.editMode); - } - }); - vm.$watch("showVisibleOnly", function() { - getPageData(1); - }) - } + addProblem: function () { + vm.$fire("up!showContestProblemPage", 0, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode); + }, + showProblemEditPage: function (el) { + vm.$fire("up!showContestProblemPage", el.id, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode); + }, + showSubmissionPage: function (el) { + var problemId = 0 + if (el) + problemId = el.id; + vm.$fire("up!showContestSubmissionPage", problemId, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode); + } + }); + vm.$watch("showVisibleOnly", function () { + getPageData(1); + }) + } getPageData(1); //init time picker @@ -293,39 +259,40 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", } // Get group list - $.ajax({ // Get current user type + $.ajax({ 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}); + var admin_type = data.data.admin_type; + if (data.data.admin_type == 1) { + vm.isGlobal = false; + vm.showGlobalViewRadio = 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); - } + } + $.ajax({ + url: "/api/admin/group/", + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + if (!data.data.length) { + if (admin_type != 2) + bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组"); + return; } - else { - bsAlert(data.data); + for (var i = 0; i < data.data.length; i++) { + var item = data.data[i]; + item["isSelected"] = false; + vm.allGroups.push(item); } } - }); - } + else { + bsAlert(data.data); + } + } + }); } }); diff --git a/static/src/js/build.js b/static/src/js/build.js index 9927a1b1..ddd9bb32 100644 --- a/static/src/js/build.js +++ b/static/src/js/build.js @@ -1,8 +1,9 @@ ({ // RequireJS 通过一个相对的路径 baseUrl来加载所有代码。baseUrl通常被设置成data-main属性指定脚本的同级目录。 - baseUrl: "js/", + baseUrl: "/static/js/", // 第三方脚本模块的别名,jquery比libs/jquery-1.11.1.min.js简洁明了; paths: { + jquery: "lib/jquery/jquery", avalon: "lib/avalon/avalon", editor: "utils/editor", @@ -37,12 +38,12 @@ webUploader: "lib/webuploader/webuploader", "_datetimePicker": "lib/datetime_picker/bootstrap-datetimepicker" - }, shim: { - "bootstrap": {"deps": ['jquery']}, - "_datetimepicker": {"deps": ["jquery"]}, - "datetimepicker": {"deps": ["_datetimepicker"]} + bootstrap: {deps: ["jquery"]}, + _datetimePicker: {dep: ["jquery"]}, + datetimePicker: {deps: ["_datetimePicker"]}, + validator: ["jquery"] }, findNestedDependencies: true, appDir: "../", diff --git a/static/src/js/lib/simditor/uploader.js b/static/src/js/lib/simditor/uploader.js index d96860ec..fc039d76 100644 --- a/static/src/js/lib/simditor/uploader.js +++ b/static/src/js/lib/simditor/uploader.js @@ -143,6 +143,16 @@ Uploader = (function(superClass) { processData: false, contentType: false, type: 'POST', + beforeSend: function(){ + var name = "csrftoken="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') c = c.substring(1); + if (c.indexOf(name) != -1) name = c.substring(name.length, c.length); + } + arguments[0].setRequestHeader("X-CSRFToken", name); + }, headers: { 'X-File-Name': encodeURIComponent(file.name) }, diff --git a/static/src/js/utils/editor.js b/static/src/js/utils/editor.js index bb09bb46..53a7d557 100644 --- a/static/src/js/utils/editor.js +++ b/static/src/js/utils/editor.js @@ -8,7 +8,7 @@ define("editor", ["simditor"], function(Simditor){ toolbarFloat: false, defaultImage: null, upload: { - url: "", + url: "/api/admin/upload_image/", params: null, fileKey: "image", connectionCount: 3, diff --git a/template/src/admin/contest/add_contest.html b/template/src/admin/contest/add_contest.html index b374ec82..54d16d8f 100644 --- a/template/src/admin/contest/add_contest.html +++ b/template/src/admin/contest/add_contest.html @@ -7,7 +7,7 @@
+ data-error="请填写比赛名称(名称不能超过50个字)" required>
@@ -26,7 +26,6 @@
-
@@ -35,61 +34,66 @@
-
-
- + + +
+ + + + + + +
+
-
+
-
-
{{el.name}}
+
+ +
+   {{ el.name }} +
-
+
-
-
- -
-
- -
-
-
+
-
+
-
diff --git a/template/src/admin/contest/contest_list.html b/template/src/admin/contest/contest_list.html index 945d1f52..bde5ae4d 100644 --- a/template/src/admin/contest/contest_list.html +++ b/template/src/admin/contest/contest_list.html @@ -24,7 +24,7 @@ {{ el.id }} {{ el.title }} - + {{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}} {{ el.created_by.username }} @@ -63,7 +63,7 @@
-

请填写比赛描述

+

请填写比赛描述

@@ -87,32 +87,33 @@
- -
- + + + + + + +
-
+
-
-
{{el.name}}
-
-
- +
+ +
+   {{ el.name }} +
- -
-
- -
-
@@ -120,34 +121,31 @@ ACM -
-
- -
-
- -
-
- - +
+
+
+
+ +
+
@@ -172,7 +170,7 @@ {{ el.sort_index }} {{ el.title }} - {{ el.score}} + {{ el.score }} {{ el.create_time|date("yyyy-MM-dd HH:mm:ss") }} diff --git a/template/src/oj/contest/contest_list.html b/template/src/oj/contest/contest_list.html index 25b8eae9..51e522db 100644 --- a/template/src/oj/contest/contest_list.html +++ b/template/src/oj/contest/contest_list.html @@ -48,12 +48,13 @@ {% endfor %} - + {% if request.user.is_authenticated %}
+ {% endif %}
- {% if submissions %} @@ -64,6 +63,7 @@ + {% if submissions %} {% for item in submissions %} @@ -94,10 +94,11 @@ {% endfor %} + {% else %} +

本场比赛还没有提交记录

+ {% endif %}
- {% else %} -

你还没有提交记录!

- {% endif %} +
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/template/src/oj/contest/submissions_list_admin.html b/template/src/oj/contest/submissions_list_admin.html new file mode 100644 index 00000000..8f1660b8 --- /dev/null +++ b/template/src/oj/contest/submissions_list_admin.html @@ -0,0 +1,114 @@ +{% extends 'oj_base.html' %} + +{% block body %} + + {% load submission %} + {% load user %} +
+
+ +
+ + + + + + + + + + + + + {% if submissions %} + + + {% for item in submissions %} + + + + + + + + + + {% endfor %} + + {% else %} +

本场比赛还没有提交记录

+ {% endif %} +
#题目名称用户提交时间 + + 运行时间 + +
+ {{ forloop.counter |add:start_id }} + {{ item.title }} + {{ item.user_id|get_username }}{{ item.create_time }} + {{ item.language|translate_language }} + + {% if item.accepted_answer_time %} + {{ item.accepted_answer_time }}ms + {% else %} + -- + {% endif %} + + {{ item.result|translate_result }} +
+ + +
+{% endblock %} \ No newline at end of file diff --git a/template/src/oj/problem/problem.html b/template/src/oj/problem/problem.html index f6833436..2ea79140 100644 --- a/template/src/oj/problem/problem.html +++ b/template/src/oj/problem/problem.html @@ -16,7 +16,7 @@
-

{{ problem.description|safe }}

+
{{ problem.description|safe }}
@@ -48,7 +48,7 @@ {% if problem.hint %}
-

{{ problem.hint|safe }}

+
{{ problem.hint|safe }}
{% endif %}
diff --git a/utils/views.py b/utils/views.py new file mode 100644 index 00000000..87beeb4f --- /dev/null +++ b/utils/views.py @@ -0,0 +1,33 @@ +# coding=utf-8 + +from rest_framework.views import APIView +from rest_framework.response import Response +from django.conf import settings + +from utils.shortcuts import rand_str + + +class SimditorImageUploadAPIView(APIView): + def post(self, request): + if "image" not in request.FILES: + return Response(data={ + "success": False, + "msg": "上传失败", + "file_path": "/"}) + img = request.FILES["image"] + + image_name = rand_str() + '.' + str(request.FILES["image"].name.split('.')[-1]) + image_dir = settings.IMAGE_UPLOAD_DIR + image_name + try: + with open(image_dir, "wb") as imageFile: + for chunk in img: + imageFile.write(chunk) + except IOError: + return Response(data={ + "success": True, + "msg": "上传错误", + "file_path": "/static/upload_image/" + image_name}) + return Response(data={ + "success": True, + "msg": "", + "file_path": "/static/upload_image/" + image_name})