Merge branch 'dev' into virusdefender-dev
* dev: (21 commits) [前端]整理格式,去掉tab(以前用vim,它自己给加的),去掉调试用的console.log[CI SKIP] [前端]统一admin中js命名方式. 为提交列表添加返回按钮[CI SKIP] [前端]修复bug,更正了不恰当的foreach循环,(js里for(var key in array)不仅遍历了数组元素,还将遍历数组其他的属性以及成员方法),修复了显示编辑区函数对选中小组错误的清除方法.(原来的做法将导致某些情况下旧的小组无法移除编辑区域. 增添了切换编辑比赛的提示,防止用户丢失为保存的信息. 添加问题列表对可见比赛的筛选[CI SKIP] [前端-BUG]修复比赛编辑区可见状态显示错误,(忘记加vm.),增加编辑成功隐藏编辑框的行为,更加方便[CI SKIP] [前端]添加比赛题目列表可见字段的显示,方便比赛管理[CI SKIP] [BUG-fix]返回按钮提示确认,修复不能弹出的问题[CI SKIP] 修复typo in submission/views.py Swagger UI docs中的拼写错误[CI SKIP] [前端]修复userList.js中关于翻页按钮状态控制函数参数的错误. 修复刚刚提交的bug[CI SKIP] [前端]修复userList页面avalon重定义问题[CI SKIP] [前端]修复问题管理(后台)页面的avalon重复定义的问题[CI SKIP] [前端]整理js格式. 修复小bugs,关于比赛密码修改变量名称的错误,小组修改变量名称错误(以上都是在修改比赛页面内)[CI SKIP] [后台]修复contestAdmin,比赛和问题API的逻辑问题,主要针对超级管理员和普通管理员的差别.写了测试,是两个api测试覆盖率达100% [migration]改model漏了一个.....[CI SKIP] [前端-后台]比赛管理,对添加,编辑,列表页面的avalon使用方法做了统一的改变,防止出现页内模板改变但页面不刷新的情况下导致avalon功能间歇性异常的问题,但是代码量变大了一些,还算是整洁.具体是所有页面的avalon只在页面第一次加载的时候初始化,再次加载时只对vm内部变量重新初始化,而不调用avalon.define了[CI SKIP] [后端]添加修改比赛题目添加对题目分数的支持 [后端]为比赛problem model添加分数(score)字段,用于记分模式的比赛 [后端]修复typo,工作正常,没写测试还 [前端]修改比赛列表页面,添加了编辑比赛,编辑比赛题目[CI SKIP] [前端]把添加比赛和添加比赛问题分开了,就是把添加问题模块从添加比赛页面删除了 [前端]添加了后台比赛列表对问题的添加修改页面[CI SKIP] ... Conflicts: static/src/js/app/admin/problem/editProblem.js static/src/js/app/admin/problem/submissionList.js submission/views.py
This commit is contained in:
19
contest/migrations/0005_contestproblem_score.py
Normal file
19
contest/migrations/0005_contestproblem_score.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0004_remove_contestproblem_difficulty'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contestproblem',
|
||||
name='score',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
]
|
||||
15
contest/migrations/0006_merge.py
Normal file
15
contest/migrations/0006_merge.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contest', '0005_contestsubmission'),
|
||||
('contest', '0005_contestproblem_score'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user