From a5211f50da1ad536786062ce8ea87efc9aa731c6 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Mon, 17 Aug 2015 20:54:10 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E7=82=B9=E5=87=BB=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- template/oj/problem/problem.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/oj/problem/problem.html b/template/oj/problem/problem.html index 2b8dfa32..5a7d8474 100644 --- a/template/oj/problem/problem.html +++ b/template/oj/problem/problem.html @@ -4,7 +4,7 @@
{% include "oj/problem/_problem_header.html" %} From 937c382b2590c4177caaedf09164a49932cdf162 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Tue, 18 Aug 2015 14:59:00 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E6=AF=94=E8=B5=9B?= =?UTF-8?q?=E5=92=8C=E6=AF=94=E8=B5=9B=E9=A2=98=E7=9B=AE=E7=9A=84=E9=83=A8?= =?UTF-8?q?=E5=88=86=EF=BC=8C=E8=BF=98=E6=9C=89=E8=A6=81=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=92=8C=E4=BF=AE=E6=94=B9=E7=9A=84=E5=9C=B0=E6=96=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/serializers.py | 95 +++++++++++++++++++++++ contest/views.py | 169 ++++++++++++++++++++++++++++++++++++++++- problem/models.py | 4 +- problem/tests.py | 5 ++ 4 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 contest/serializers.py diff --git a/contest/serializers.py b/contest/serializers.py new file mode 100644 index 00000000..424ba0ef --- /dev/null +++ b/contest/serializers.py @@ -0,0 +1,95 @@ +# coding=utf-8 +import json +from rest_framework import serializers + +from account.models import User +from .models import Contest, ContestProblem + + +class CreateContestSerializer(serializers.Serializer): + title = serializers.CharField(max_length=40) + description = serializers.CharField(max_length=5000) + mode = serializers.IntegerField() + show_rank = serializers.BooleanField() + password = serializers.CharField(max_length=30, required=False) + start_time = serializers.DateTimeField() + end_time = serializers.DateTimeField() + + +class ContestSerializer(serializers.ModelSerializer): + class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ["username"] + + created_by = UserSerializer() + + class Meta: + model = Contest + + +class EditContestSerializer(serializers.Serializer): + title = serializers.CharField(max_length=40) + description = serializers.CharField(max_length=10000) + mode = serializers.IntegerField() + show_rank = serializers.BooleanField() + password = serializers.CharField(max_length=30, required=False) + start_time = serializers.DateTimeField() + end_time = serializers.DateTimeField() + + +class ContestProblemSampleSerializer(serializers.ListField): + input = serializers.CharField(max_length=3000) + output = serializers.CharField(max_length=3000) + + +class JSONField(serializers.Field): + def to_representation(self, value): + return json.loads(value) + + +class CreateContestProblemSerializer(serializers.Serializer): + title = serializers.CharField(max_length=50) + description = serializers.CharField(max_length=10000) + input_description = serializers.CharField(max_length=10000) + output_description = serializers.CharField(max_length=10000) + # [{"input": "1 1", "output": "2"}] + samples = ContestProblemSampleSerializer() + test_case_id = serializers.CharField(max_length=40) + time_limit = serializers.IntegerField() + memory_limit = serializers.IntegerField() + difficulty = serializers.IntegerField() + hint = serializers.CharField(max_length=3000, allow_blank=True) + sort_index = serializers.CharField(max_length=30) + + +class ContestProblemSerializer(serializers.ModelSerializer): + samples = JSONField() + + class ContestSerializer(serializers.ModelSerializer): + class Meta: + model = Contest + fields = ["title"] + + created_by = ContestSerializer() + + class Meta: + model = ContestProblem + + +class EditContestProblemSerializer(serializers.Serializer): + id = serializers.IntegerField() + title = serializers.CharField(max_length=50) + description = serializers.CharField(max_length=10000) + input_description = serializers.CharField(max_length=10000) + output_description = serializers.CharField(max_length=10000) + test_case_id = serializers.CharField(max_length=40) + time_limit = serializers.IntegerField() + memory_limit = serializers.IntegerField() + difficulty = serializers.IntegerField() + samples = ContestProblemSampleSerializer() + hint = serializers.CharField(max_length=3000, allow_blank=True) + visible = serializers.BooleanField() + sort_index = serializers.CharField(max_length=30) + + diff --git a/contest/views.py b/contest/views.py index 91ea44a2..3746b2bf 100644 --- a/contest/views.py +++ b/contest/views.py @@ -1,3 +1,170 @@ +# coding=utf-8 +import json from django.shortcuts import render +from django.db import IntegrityError +from django.db.models import Q +from rest_framework.views import APIView -# Create your views here. +from .models import Contest, ContestProblem +from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer, + CreateContestProblemSerializer, ContestProblemSerializer, EditContestProblemSerializer) +from utils.shortcuts import (serializer_invalid_response, error_response, + success_response, paginate, rand_str, error_page) + + +def contest_page(request, contest_id): + pass + + +class ContestAdminAPIView(APIView): + def post(self, request): + """ + 比赛发布json api接口 + --- + request_serializer: CreateContestSerializer + response_serializer: ContestSerializer + """ + serializer = CreateContestSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + try: + contest = Contest.objects.create(title=data["title"], description=data["description"], + mode=data["mode"], show_rank=data["show_rank"], + start_time=["start_time"], end_time=data["end_time"]) + if data["password"]: + contest.setpassword(data["password"]) + contest.save() + except IntegrityError: + return error_response(u"比赛名已经存在") + return success_response(ContestSerializer(contest).data) + else: + serializer_invalid_response(serializer) + + def put(self, request): + """ + 比赛编辑json api接口 + --- + request_serializer: EditContestSerializer + response_serializer: ContestSerializer + """ + serializer = EditContestSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + try: + contest = Contest.objects.get(id=data["id"]) + except Contest.DoesNotExist: + error_response(u"该比赛不存在!") + try: + contest.title = data["contest"] + contest.description = data["description"] + contest.mode = data["mode"] + contest.show_rank = data["show_"] + if data["password"]: + contest.set_password(data["password"]) + contest.save() + except IntegrityError: + return error_response(u"比赛名已经存在") + + return success_response(ContestSerializer(contest).data) + else: + serializer_invalid_response(serializer) + + def get(self, request): + """ + 比赛分页json api接口 + --- + response_serializer: ContestSerializer + """ + contest = Contest.objects.all().order_by("-last_updated_time") + visible = request.GET.get("visible", None) + if visible: + contest = contest.filter(visible=(visible == "true")) + keyword = request.GET.get("keyword", None) + if keyword: + contest = contest.filter(Q(title__contains=keyword) | + Q(description__contains=keyword)) + return paginate(request, contest, ContestSerializer) + + +class ContestProblemAdminAPIView(APIView): + def post(self, request): + """ + 比赛题目发布json api接口 + --- + request_serializer: CreateContestProblemSerializer + response_serializer: ContestProblemSerializer + """ + serializer = CreateContestProblemSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + contest_problem = ContestProblem.objects.create(title=data["title"], + description=data["description"], + input_description=data["input_description"], + output_description=data["output_description"], + test_case_id=data["test_case_id"], + samples=json.dumps(data["samples"]), + time_limit=data["time_limit"], + memory_limit=data["memory_limit"], + difficulty=data["difficulty"], + created_by=request.user, + hint=data["hint"], + contest=request.contest, + sort_index=data["sort_index"]) + return success_response(ContestProblemSerializer(contest_problem).data) + else: + serializer_invalid_response(serializer) + + def put(self, request): + """ + 比赛题目编辑json api接口 + --- + request_serializer: EditContestSerializer + response_serializer: ContestProblemSerializer + """ + serializer = EditContestSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + try: + contest_problem = ContestProblem.objects.get(id=data["id"]) + except ContestProblem.DoesNotExist: + return error_response(u"该比赛题目不存在!") + contest_problem.title = data["title"] + contest_problem.description = data["description"] + contest_problem.input_description = data["input_description"] + contest_problem.output_description = data["output_description"] + contest_problem.test_case_id = data["test_case_id"] + contest_problem.time_limit = data["time_limit"] + contest_problem.memory_limit = data["memory_limit"] + contest_problem.difficulty = data["difficulty"] + contest_problem.samples = json.dumps(data["samples"]) + contest_problem.hint = data["hint"] + contest_problem.visible = data["visible"] + contest_problem.sort_index = data["sort_index"] + contest_problem.save() + return success_response(ContestProblem(contest_problem).data) + else: + return serializer_invalid_response(serializer) + + def get(self, request): + """ + 比赛题目分页json api接口 + --- + response_serializer: ProblemSerializer + """ + contest_problem_id = request.GET.get("contest_problem_id", None) + if contest_problem_id: + try: + contest_problem = ContestProblemSerializer.objects.get(id=contest_problem_id) + return success_response(ContestProblemSerializer(contest_problem).data) + except contest_problem.DoesNotExist: + return error_response(u"比赛题目不存在") + contest_problem = ContestProblem.objects.all().order_by("sort_index") + visible = request.GET.get("visible", None) + if visible: + contest_problem = contest_problem.filter(visible=(visible == "true")) + keyword = request.GET.get("keyword", None) + if keyword: + contest_problem = contest_problem.filter(Q(title__contains=keyword) | + Q(description__contains=keyword)) + + return paginate(request, contest_problem, ContestProblemSerializer) \ No newline at end of file diff --git a/problem/models.py b/problem/models.py index d6ac77bb..dbc886b6 100644 --- a/problem/models.py +++ b/problem/models.py @@ -32,8 +32,6 @@ class AbstractProblem(models.Model): # last_update_time = models.DateTimeField(auto_now=True) # 这个题是谁创建的 created_by = models.ForeignKey(User) - # 来源 - source = models.CharField(max_length=30, blank=True, null=True) # 时间限制 单位是毫秒 time_limit = models.IntegerField() # 内存限制 单位是MB @@ -54,3 +52,5 @@ class Problem(AbstractProblem): difficulty = models.IntegerField() # 标签 tags = models.ManyToManyField(ProblemTag) + # 来源 + source = models.CharField(max_length=30, blank=True, null=True) diff --git a/problem/tests.py b/problem/tests.py index 91affaea..925ffe08 100644 --- a/problem/tests.py +++ b/problem/tests.py @@ -164,3 +164,8 @@ class ProblemTagAdminAPITest(APITestCase): def test_get_all_problem_tag_successfully(self): self.assertEqual(self.client.get(self.url).data["code"], 0) + +class ProblemListPageTest(TestCase): + def setUp(self): + pass + From bdf3fa9ed997967a2da7a967cb6333e765aea900 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Tue, 18 Aug 2015 20:12:27 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=AF=94=E8=B5=9B=E5=88=9B=E5=BB=BA=E9=83=A8=E5=88=86=EF=BC=8C?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E5=9C=A8models=E4=B8=AD=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=BA=86=E5=87=A0=E4=B8=AA=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/models.py | 16 +++++++++++++--- contest/serializers.py | 6 ++++++ contest/views.py | 40 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/contest/models.py b/contest/models.py index 56762ecb..eb0f5e68 100644 --- a/contest/models.py +++ b/contest/models.py @@ -3,18 +3,25 @@ from django.db import models from account.models import User from problem.models import AbstractProblem +from group.models import Group class Contest(models.Model): title = models.CharField(max_length=40) description = models.TextField() - # 比赛模式 现在有 acm 模式,按照 ac 题目数量得分模式, - # 按照 ac 的题目的总分得分模式和按照通过的测试用例总分得分模式等 + # 比赛模式:0 即为是acm模式,1 即为是按照总的 ac 题目数量排名模式,2 即为按照 ac 的题目的总分排名模式 mode = models.IntegerField() # 是否显示排名结果 show_rank = models.BooleanField() - # 如果这一项不为空,那这就不是公开赛,需要密码才能进入 + # 是否显示别人的提交记录 + show_user_submission = models.BooleanField() + + # 只能超级管理员创建公开赛,管理员只能创建小组内部的比赛 + # 如果这一项不为空,即为有密码的公开赛,没有密码的可以为小组赛或者是公开赛(此时用比赛的类型来表示) password = models.CharField(max_length=30, blank=True, null=True) + # 比赛的类型: 0 即为是小组赛,1 即为是无密码的公开赛,2 即为是有密码的公开赛 + contest_type = models.IntegerField() + # 开始时间 start_time = models.DateTimeField() # 结束时间 @@ -23,7 +30,10 @@ class Contest(models.Model): create_time = models.DateTimeField(auto_now_add=True) # 最后修改时间 last_updated_time = models.DateTimeField(auto_now=True) + # 这个比赛是谁创建的 created_by = models.ForeignKey(User) + groups = models.ManyToManyField(Group) + class Meta: db_table = "contest" diff --git a/contest/serializers.py b/contest/serializers.py index 424ba0ef..51a4066a 100644 --- a/contest/serializers.py +++ b/contest/serializers.py @@ -10,10 +10,13 @@ class CreateContestSerializer(serializers.Serializer): title = serializers.CharField(max_length=40) description = serializers.CharField(max_length=5000) mode = serializers.IntegerField() + contest_type = serializers.IntegerField() show_rank = serializers.BooleanField() + show_user_submission = serializers.BooleanField() password = serializers.CharField(max_length=30, required=False) start_time = serializers.DateTimeField() end_time = serializers.DateTimeField() + groups = serializers.ListField(child=serializers.IntegerField()) class ContestSerializer(serializers.ModelSerializer): @@ -32,10 +35,13 @@ class EditContestSerializer(serializers.Serializer): title = serializers.CharField(max_length=40) description = serializers.CharField(max_length=10000) mode = serializers.IntegerField() + contest_type = serializers.IntegerField() show_rank = serializers.BooleanField() + show_user_submission = serializers.BooleanField() password = serializers.CharField(max_length=30, required=False) start_time = serializers.DateTimeField() end_time = serializers.DateTimeField() + groups = serializers.ListField(child=serializers.IntegerField()) class ContestProblemSampleSerializer(serializers.ListField): diff --git a/contest/views.py b/contest/views.py index 3746b2bf..243d4799 100644 --- a/contest/views.py +++ b/contest/views.py @@ -4,12 +4,16 @@ from django.shortcuts import render from django.db import IntegrityError from django.db.models import Q 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 group.models import Group from .models import Contest, ContestProblem from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer, CreateContestProblemSerializer, ContestProblemSerializer, EditContestProblemSerializer) -from utils.shortcuts import (serializer_invalid_response, error_response, - success_response, paginate, rand_str, error_page) + def contest_page(request, contest_id): @@ -27,15 +31,47 @@ class ContestAdminAPIView(APIView): serializer = CreateContestSerializer(data=request.data) if serializer.is_valid(): data = serializer.data + groups = [] + # 首先判断比赛的类型: 0 即为是小组赛,1 即为是无密码的公开赛,2 即为是有密码的公开赛 + # 密码字段不为空的情况,此时为有密码的公开赛, + # 此时为有密码的公开赛,并且此时只能超级管理员才有权限此创建比赛 + if data["contest_type"] == 2: + if data["password"]: + if request.user.admin_type == SUPER_ADMIN: + pass + else: + return error_response(u"只有超级管理员才可创建公开赛(有密码)") + else: + return error_response(u"此比赛为有密码的公开赛,密码不可为空") + # 此时为没有密码的公开赛,并且此时只能是超级管理员才有权限创建此比赛 + elif data["contest_type"]: + if not data["password"]: + if request.user.admin_type == SUPER_ADMIN: + pass + else: + return error_response(u"只有超级管理员才可创建公开赛(没有密码)") + else: + return error_response(u"此比赛为没有密码的公开赛,密码无需填写") + # 此时为没有密码的小组赛,并且此时只能是超级管理员和管理员才有权限创建此比赛 + else: + if request.user.admin_type == REGULAR_USER: + return error_response(u"只有超级管理员和管理员才有权限创建比赛") + else: + if not data["password"]: + groups = Group.objects.filter(id__in=data["groups"], admin=request.user) + else: + return error_response(u"此比赛为没有密码的小组赛,密码无需填写") try: contest = Contest.objects.create(title=data["title"], description=data["description"], mode=data["mode"], show_rank=data["show_rank"], + show_user_submission=data["show_user_submission"], start_time=["start_time"], end_time=data["end_time"]) if data["password"]: contest.setpassword(data["password"]) contest.save() except IntegrityError: return error_response(u"比赛名已经存在") + contest.groups.add(*groups) return success_response(ContestSerializer(contest).data) else: serializer_invalid_response(serializer) From 10e4d534b292030ba1d4d2439dc7f45ef438a11a Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Wed, 19 Aug 2015 17:53:43 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=AF=94=E8=B5=9B?= =?UTF-8?q?=E7=9A=84views=E5=8F=8A=E9=83=A8=E5=88=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- announcement/serializers.py | 2 +- announcement/views.py | 4 +- contest/models.py | 3 +- contest/views.py | 93 ++++++++++++++++++++----------------- 4 files changed, 54 insertions(+), 48 deletions(-) diff --git a/announcement/serializers.py b/announcement/serializers.py index d896564a..7fffe045 100644 --- a/announcement/serializers.py +++ b/announcement/serializers.py @@ -9,7 +9,7 @@ class CreateAnnouncementSerializer(serializers.Serializer): title = serializers.CharField(max_length=50) content = serializers.CharField(max_length=10000) is_global = serializers.BooleanField() - groups = serializers.ListField(child=serializers.IntegerField(), allow_empty=True) + groups = serializers.ListField(child=serializers.IntegerField()) class AnnouncementSerializer(serializers.ModelSerializer): diff --git a/announcement/views.py b/announcement/views.py index 8059b908..73f49c8e 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -68,7 +68,7 @@ class AnnouncementAdminAPIView(APIView): if request.user.admin_type == SUPER_ADMIN: announcement = Announcement.objects.get(id=data["id"]) else: - announcement = Announcement.objects.get(id=data["id"], admin=request.user) + announcement = Announcement.objects.get(id=data["id"], created_by=request.user) except Announcement.DoesNotExist: return error_response(u"公告不存在") groups = [] @@ -101,7 +101,7 @@ class AnnouncementAdminAPIView(APIView): if request.user.admin_type == SUPER_ADMIN: announcement = Announcement.objects.all().order_by("-last_update_time") else: - announcement = Announcement.objects.filter(admin=request.user) + announcement = Announcement.objects.filter(created_by=request.user) visible = request.GET.get("visible", None) if visible: announcement = announcement.filter(visible=(visible == "true")) diff --git a/contest/models.py b/contest/models.py index eb0f5e68..838d6d90 100644 --- a/contest/models.py +++ b/contest/models.py @@ -7,7 +7,7 @@ from group.models import Group class Contest(models.Model): - title = models.CharField(max_length=40) + title = models.CharField(max_length=40, unique=True) description = models.TextField() # 比赛模式:0 即为是acm模式,1 即为是按照总的 ac 题目数量排名模式,2 即为按照 ac 的题目的总分排名模式 mode = models.IntegerField() @@ -34,7 +34,6 @@ class Contest(models.Model): created_by = models.ForeignKey(User) groups = models.ManyToManyField(Group) - class Meta: db_table = "contest" diff --git a/contest/views.py b/contest/views.py index 243d4799..31ed0f44 100644 --- a/contest/views.py +++ b/contest/views.py @@ -33,48 +33,34 @@ class ContestAdminAPIView(APIView): data = serializer.data groups = [] # 首先判断比赛的类型: 0 即为是小组赛,1 即为是无密码的公开赛,2 即为是有密码的公开赛 - # 密码字段不为空的情况,此时为有密码的公开赛, # 此时为有密码的公开赛,并且此时只能超级管理员才有权限此创建比赛 + if data["contest_type"] in [1, 2]: + if request.user.admin_type != SUPER_ADMIN: + return error_response(u"只有超级管理员才可创建公开赛") if data["contest_type"] == 2: - if data["password"]: - if request.user.admin_type == SUPER_ADMIN: - pass - else: - return error_response(u"只有超级管理员才可创建公开赛(有密码)") - else: - return error_response(u"此比赛为有密码的公开赛,密码不可为空") - # 此时为没有密码的公开赛,并且此时只能是超级管理员才有权限创建此比赛 - elif data["contest_type"]: if not data["password"]: - if request.user.admin_type == SUPER_ADMIN: - pass - else: - return error_response(u"只有超级管理员才可创建公开赛(没有密码)") + return error_response(u"此比赛为有密码的公开赛,密码不可为空") + + # 没有密码的公开赛 没有密码的小组赛 + elif data["contest_type"] == 0: + if request.user.admin_type == SUPER_ADMIN: + groups = Group.objects.filter(id__in=data["groups"]) else: - return error_response(u"此比赛为没有密码的公开赛,密码无需填写") - # 此时为没有密码的小组赛,并且此时只能是超级管理员和管理员才有权限创建此比赛 - else: - if request.user.admin_type == REGULAR_USER: - return error_response(u"只有超级管理员和管理员才有权限创建比赛") - else: - if not data["password"]: - groups = Group.objects.filter(id__in=data["groups"], admin=request.user) - else: - return error_response(u"此比赛为没有密码的小组赛,密码无需填写") + groups = Group.objects.filter(id__in=data["groups"], admin=request.user) + if not groups.count(): + return error_response(u"请至少选择一个小组") try: contest = Contest.objects.create(title=data["title"], description=data["description"], mode=data["mode"], show_rank=data["show_rank"], show_user_submission=data["show_user_submission"], - start_time=["start_time"], end_time=data["end_time"]) - if data["password"]: - contest.setpassword(data["password"]) - contest.save() + start_time=["start_time"], end_time=data["end_time"], + password=data["password"]) except IntegrityError: return error_response(u"比赛名已经存在") contest.groups.add(*groups) return success_response(ContestSerializer(contest).data) else: - serializer_invalid_response(serializer) + return serializer_invalid_response(serializer) def put(self, request): """ @@ -86,24 +72,45 @@ class ContestAdminAPIView(APIView): serializer = EditContestSerializer(data=request.data) if serializer.is_valid(): data = serializer.data + groups = [] try: contest = Contest.objects.get(id=data["id"]) except Contest.DoesNotExist: - error_response(u"该比赛不存在!") + return error_response(u"该比赛不存在!") try: - contest.title = data["contest"] - contest.description = data["description"] - contest.mode = data["mode"] - contest.show_rank = data["show_"] - if data["password"]: - contest.set_password(data["password"]) - contest.save() - except IntegrityError: - return error_response(u"比赛名已经存在") + contest = Contest.objects.get(title=data["title"]) + if contest.id != data["id"]: + return error_response(u"该比赛名称已经存在") + except Contest.DoesNotExist: + pass + if data["contest_type"] in [1, 2]: + if request.user.admin_type != SUPER_ADMIN: + return error_response(u"只有超级管理员才可创建公开赛") + if data["contest_type"] == 2: + if not data["password"]: + return error_response(u"此比赛为有密码的公开赛,密码不可为空") + elif data["contest_type"] == 0: + if request.user.admin_type == SUPER_ADMIN: + groups = Group.objects.filter(id__in=data["groups"]) + else: + groups = Group.objects.filter(id__in=data["groups"], admin=request.user) + if not groups.count(): + return error_response(u"请至少选择一个小组") + contest.title = data["title"] + contest.description = data["description"] + contest.mode = data["mode"] + contest.show_rank = data["show_rank"] + contest.show_user_submission = data["show_user_submission"] + contest.start_time = ["start_time"] + contest.end_time = data["end_time"] + contest.password = data["password"] + contest.save() + contest.groups.clear() + contest.groups.add(*groups) return success_response(ContestSerializer(contest).data) else: - serializer_invalid_response(serializer) + return serializer_invalid_response(serializer) def get(self, request): """ @@ -148,7 +155,7 @@ class ContestProblemAdminAPIView(APIView): sort_index=data["sort_index"]) return success_response(ContestProblemSerializer(contest_problem).data) else: - serializer_invalid_response(serializer) + return serializer_invalid_response(serializer) def put(self, request): """ @@ -190,9 +197,9 @@ class ContestProblemAdminAPIView(APIView): contest_problem_id = request.GET.get("contest_problem_id", None) if contest_problem_id: try: - contest_problem = ContestProblemSerializer.objects.get(id=contest_problem_id) + contest_problem = ContestProblem.objects.get(id=contest_problem_id) return success_response(ContestProblemSerializer(contest_problem).data) - except contest_problem.DoesNotExist: + except ContestProblem.DoesNotExist: return error_response(u"比赛题目不存在") contest_problem = ContestProblem.objects.all().order_by("sort_index") visible = request.GET.get("visible", None) From 0fcbbc962e6c697388b246e81fb82029a9b85e14 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Thu, 20 Aug 2015 20:24:51 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8F=8A=E4=BF=AE?= =?UTF-8?q?=E6=94=B9account=E4=B8=8Eannouncement=E7=9A=84=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/tests.py | 68 +++++++++++++------ announcement/serializers.py | 4 +- announcement/tests.py | 129 +++++++++++++++++++++++++----------- announcement/views.py | 1 - oj/settings.py | 5 ++ 5 files changed, 145 insertions(+), 62 deletions(-) diff --git a/account/tests.py b/account/tests.py index 10070fa6..a84911c0 100644 --- a/account/tests.py +++ b/account/tests.py @@ -130,7 +130,7 @@ class UserChangePasswordAPITest(APITestCase): self.assertEqual(response.data, {"code": 1, "data": u"密码不正确,请重新修改!"}) def test_invalid_data_format(self): - data = {"username": "test", "old_password": "aaa", "new_password": "aaaddd"} + data = {"old_password": "aaa", "new_password": "aaaddd"} response = self.client.post(self.url, data=data) self.assertEqual(response.data["code"], 1) @@ -152,15 +152,50 @@ class UserAdminAPITest(APITestCase): user = User.objects.create(username="testx", real_name="xx", admin_type=SUPER_ADMIN) user.set_password("testxx") user.save() + user = User.objects.create(username="testy", real_name="yy", admin_type=SUPER_ADMIN) + user.set_password("testyy") + user.save() self.client.login(username="testx", password="testxx") + # 以下是编辑用户的测试 def test_success_get_data(self): self.assertEqual(self.client.get(self.url).data["code"], 0) + def test_put_invalid_data(self): + data = {"username": "test", "password": "testaa", "email": "60@qq.com", "admin_type": "2"} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 1) + + def test_user_does_not_exist(self): + data = {"id": 3, "username": "test0", "real_name": "test00", + "password": "testaa", "email": "60@qq.com", "admin_type": "2"} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"该用户不存在!"}) + + def test_username_exists(self): + data = {"id": 1, "username": "testy", "real_name": "test00", + "password": "testaa", "email": "60@qq.com", "admin_type": "2"} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"昵称已经存在"}) + + def test_user_edit_not_password_successfully(self): + data = {"id": 1, "username": "test0", "real_name": "test00", + "email": "60@qq.com", "admin_type": "2"} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 0) + + def test_user_edit_change_password_successfully(self): + data = {"id": 1, "username": "test0", "real_name": "test00", "password": "111111", + "email": "60@qq.com", "admin_type": "2"} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 0) + self.assertIsNotNone(auth.authenticate(username="test0", password="111111")) + def test_error_admin_type(self): response = self.client.get(self.url + "?admin_type=error") self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) + # 以下是用户分页的测试 def test_query_by_keyword(self): user1 = User.objects.create(username="test1", real_name="aa") user1.set_password("testaa") @@ -177,29 +212,18 @@ class UserAdminAPITest(APITestCase): response = self.client.get(self.url + "?keyword=test1") self.assertEqual(response.data["code"], 0) - def test_put_invalid_data(self): - data = {"username": "test", "password": "testaa", "email": "60@qq.com", "admin_type": "2"} - response = self.client.put(self.url, data=data) - self.assertEqual(response.data["code"], 1) - def test_user_does_not_exist(self): - data = {"id": 2, "username": "test0", "real_name": "test00", - "password": "testaa","email": "60@qq.com", "admin_type": "2"} - response = self.client.put(self.url, data=data) - self.assertEqual(response.data, {"code": 1, "data": u"该用户不存在!"}) +class UserInfoAPITest(APITestCase): + def setUp(self): + self.client = APIClient() + self.url = reverse('user_info_api') + user = User.objects.create(username="test1", real_name="aa") + user.set_password("testaa") + user.save() - def test_success_user_edit_not_password(self): - data = {"id": 1, "username": "test0", "real_name": "test00", - "email": "60@qq.com", "admin_type": "2"} - response = self.client.put(self.url, data=data) - self.assertEqual(response.data["code"], 0) - - def test_success_user_edit_change_password(self): - data = {"id": 1, "username": "test0", "real_name": "test00", "password": "111111", - "email": "60@qq.com", "admin_type": "2"} - response = self.client.put(self.url, data=data) - self.assertEqual(response.data["code"], 0) - self.assertIsNotNone(auth.authenticate(username="test0", password="111111")) + def test_get_data_successfully(self): + self.client.login(username="test1", password="testaa") + self.assertEqual(self.client.get(self.url).data["code"], 0) @login_required diff --git a/announcement/serializers.py b/announcement/serializers.py index 7fffe045..ec24116f 100644 --- a/announcement/serializers.py +++ b/announcement/serializers.py @@ -9,7 +9,7 @@ class CreateAnnouncementSerializer(serializers.Serializer): title = serializers.CharField(max_length=50) content = serializers.CharField(max_length=10000) is_global = serializers.BooleanField() - groups = serializers.ListField(child=serializers.IntegerField()) + groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[]) class AnnouncementSerializer(serializers.ModelSerializer): @@ -31,4 +31,4 @@ class EditAnnouncementSerializer(serializers.Serializer): content = serializers.CharField(max_length=10000) visible = serializers.BooleanField() is_global = serializers.BooleanField() - groups = serializers.ListField(child=serializers.IntegerField()) + groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[]) diff --git a/announcement/tests.py b/announcement/tests.py index 7ce24673..6ac3fb2a 100644 --- a/announcement/tests.py +++ b/announcement/tests.py @@ -5,6 +5,7 @@ from django.test import TestCase from rest_framework.test import APITestCase, APIClient from account.models import User +from group.models import Group from announcement.models import Announcement from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN @@ -13,76 +14,127 @@ class AnnouncementAdminAPITest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse("announcement_admin_api") - user = User.objects.create(username="test", admin_type=SUPER_ADMIN) - user.set_password("testaa") - user.save() + user1 = User.objects.create(username="test1", admin_type=SUPER_ADMIN) + user1.set_password("testaa") + user1.save() + user2 = User.objects.create(username="test2", admin_type=ADMIN) + user2.set_password("testbb") + user2.save() + self.group = Group.objects.create(name="group1", description="des0", + join_group_setting=0, visible=True, + admin=user2) + self.announcement = Announcement.objects.create(title="bb", + content="BB", + created_by=User.objects.get(username="test2"), + is_global=False) # 以下是发布公告的测试 def test_invalid_format(self): - self.client.login(username="test", password="testaa") + self.client.login(username="test1", password="testaa") data = {"title": "test1"} response = self.client.post(self.url, data=data) self.assertEqual(response.data["code"], 1) - def test_success_announcement(self): - self.client.login(username="test", password="testaa") - data = {"title": "title0", "content": "content0"} + def test_group_at_least_one(self): + self.client.login(username="test1", password="testaa") + data = {"title": "title0", "content": "content0", "is_global": False} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"至少选择一个小组"}) + + def test_global_announcement_successfully(self): + self.client.login(username="test1", password="testaa") + data = {"title": "title0", "content": "content0", "is_global": True} response = self.client.post(self.url, data=data) self.assertEqual(response.data, {"code": 0, "data": u"公告发布成功!"}) - def test_post_invalid_data(self): - self.client.login(username="test", password="testaa") - data = {"title": "test"} + def test_group_announcement_successfully(self): + self.client.login(username="test2", password="testbb") + data = {"title": "title0", "content": "content0", "is_global": False, "groups": [self.group.id]} response = self.client.post(self.url, data=data) - self.assertEqual(response.data["code"], 1) + self.assertEqual(response.data, {"code": 0, "data": u"公告发布成功!"}) + + def test_global_announcement_does_not_has_privileges(self): + self.client.login(username="test2", password="testbb") + data = {"title": "title0", "content": "content0", "is_global": True} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"只有超级管理员可以创建全局公告"}) # 以下是编辑公告的测试 def test_put_invalid_data(self): - self.client.login(username="test", password="testaa") + self.client.login(username="test1", password="testaa") data = {"title": "test0", "content": "test0", "visible": "True"} response = self.client.put(self.url, data=data) self.assertEqual(response.data["code"], 1) def test_announcement_does_not_exist(self): - self.client.login(username="test", password="testaa") + self.client.login(username="test1", password="testaa") announcement = Announcement.objects.create(title="aa", content="AA", - created_by=User.objects.get(username="test")) - data = {"id": announcement.id + 1, "title": "11", "content": "22", "visible": True} + created_by=User.objects.get(username="test1"), + is_global=True) + data = {"id": announcement.id + 1, "title": "11", "content": "22", + "visible": True, "is_global": True} response = self.client.put(self.url, data=data) - self.assertEqual(response.data, {"code": 1, "data": u"该公告不存在!"}) + self.assertEqual(response.data, {"code": 1, "data": u"公告不存在"}) - def test_success_edit_announcement(self): - self.client.login(username="test", password="testaa") - announcement = Announcement.objects.create(title="bb", - content="BB", - created_by=User.objects.get(username="test")) - data = {"id": announcement.id, "title": "11", "content": "22", "visible": True} + def test_edit_global_announcement_successfully(self): + self.client.login(username="test1", password="testaa") + data = {"id": self.announcement.id, "title": "11", "content": "22", + "visible": True, "is_global": True} response = self.client.put(self.url, data=data) self.assertEqual(response.data["code"], 0) + def test_edit_group_announcement_successfully(self): + self.client.login(username="test2", password="testbb") + data = {"id": self.announcement.id, "title": "11", "content": "22", + "visible": True, "is_global": False, "groups": [self.group.id]} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 0) -class AnnouncementAPITest(APITestCase): - def setUp(self): - self.client = APIClient() - self.url = reverse("announcement_list_api") - user = User.objects.create(username="test") - user.set_password("testaa") - user.save() + def test_edit_group_at_least_one(self): + self.client.login(username="test1", password="testaa") + data = {"id": self.announcement.id, "title": "title0", "content": "content0", + "visible": True, "is_global": False} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"至少选择一个小组"}) - def test_success_get_data(self): + # 以下是公告分页的测试 + def test_get_data_successfully(self): + self.client.login(username="test1", password="testaa") self.assertEqual(self.client.get(self.url).data["code"], 0) - def test_keyword_announcement(self): + def test_keyword_global_announcement(self): + self.client.login(username="test1", password="testaa") Announcement.objects.create(title="aa", content="AA", - created_by=User.objects.get(username="test"), - visible=True) + created_by=User.objects.get(username="test1"), + visible=True, + is_global=True) Announcement.objects.create(title="bb", content="BB", - created_by=User.objects.get(username="test"), - visible=False) + created_by=User.objects.get(username="test1"), + visible=False, + is_global=True) + + response = self.client.get(self.url + "?visible=true") + self.assertEqual(response.data["code"], 0) + for item in response.data["data"]: + self.assertEqual(item["visible"], True) + + def test_keyword_group_announcement(self): + self.client.login(username="test2", password="testbb") + Announcement.objects.create(title="aa", + content="AA", + created_by=User.objects.get(username="test2"), + visible=True, + is_global=False) + + Announcement.objects.create(title="cc", + content="CC", + created_by=User.objects.get(username="test2"), + visible=False, + is_global=False) response = self.client.get(self.url + "?visible=true") self.assertEqual(response.data["code"], 0) @@ -98,12 +150,14 @@ class AnnouncementPageTest(TestCase): Announcement.objects.create(title="aa", content="AA", created_by=User.objects.get(username="test"), - visible=True) + visible=True, + is_global=True) Announcement.objects.create(title="bb", content="BB", created_by=User.objects.get(username="test"), - visible=False) + visible=False, + is_global=True) def test_visit_announcement_successfully(self): response = self.client.get('/announcement/1/') @@ -112,3 +166,4 @@ class AnnouncementPageTest(TestCase): def test_announcement_does_not_exist(self): response = self.client.get('/announcement/3/') self.assertTemplateUsed(response, "utils/error.html") + diff --git a/announcement/views.py b/announcement/views.py index 73f49c8e..818aa0a9 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -49,7 +49,6 @@ class AnnouncementAdminAPIView(APIView): is_global=data["is_global"]) announcement.groups.add(*groups) - return success_response(u"公告发布成功!") else: return serializer_invalid_response(serializer) diff --git a/oj/settings.py b/oj/settings.py index 8d012d03..f956f5bc 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -156,3 +156,8 @@ LOGGING = { } }, } + + +REST_FRAMEWORK = { + 'TEST_REQUEST_DEFAULT_FORMAT': 'json' +} \ No newline at end of file From 33fec6531acc4a6fe59e9145a6dc30074d630db3 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Fri, 21 Aug 2015 15:52:18 +0800 Subject: [PATCH 6/8] =?UTF-8?q?CodeMirror=E8=87=AA=E5=8A=A8=E6=9B=B4?= =?UTF-8?q?=E6=94=B9=E9=AB=98=E5=BA=A6=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/css/oj.css | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/static/src/css/oj.css b/static/src/css/oj.css index cc6ac62d..b0985580 100644 --- a/static/src/css/oj.css +++ b/static/src/css/oj.css @@ -67,4 +67,19 @@ li.list-group-item { .panel>.list-group{ padding: 0 0; +} + +.CodeMirror{ + min-height: 250px; + _height:250px; + height: auto; +} + +.CodeMirror-scroll { + overflow: auto; + min-height: 250px; + _height:250px; + height: auto; + position: relative; + outline: none; } \ No newline at end of file From 53d2b674c93b0a074747dd48721ac707b495b4b6 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Fri, 21 Aug 2015 18:16:34 +0800 Subject: [PATCH 7/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B9=B6=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E6=AF=94=E8=B5=9B=E5=8F=8A=E5=85=B6=E4=BB=96=E9=83=A8?= =?UTF-8?q?=E5=88=86=E7=9A=84=E5=86=85=E5=AE=B9=E5=8F=8A=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- announcement/tests.py | 2 + contest/migrations/0001_initial.py | 74 +++++++ contest/migrations/0002_contest_visible.py | 19 ++ contest/models.py | 2 + contest/serializers.py | 11 +- contest/tests.py | 218 ++++++++++++++++++++- contest/views.py | 20 +- group/views.py | 3 +- oj/settings.py | 1 + oj/urls.py | 4 + problem/tests.py | 26 ++- problem/views.py | 1 - 12 files changed, 365 insertions(+), 16 deletions(-) create mode 100644 contest/migrations/0001_initial.py create mode 100644 contest/migrations/0002_contest_visible.py diff --git a/announcement/tests.py b/announcement/tests.py index 6ac3fb2a..e37c1834 100644 --- a/announcement/tests.py +++ b/announcement/tests.py @@ -90,6 +90,8 @@ class AnnouncementAdminAPITest(APITestCase): "visible": True, "is_global": False, "groups": [self.group.id]} response = self.client.put(self.url, data=data) self.assertEqual(response.data["code"], 0) + self.assertEqual(response.data["data"]["title"], "11") + self.assertEqual(response.data["data"]["content"], "22") def test_edit_group_at_least_one(self): self.client.login(username="test1", password="testaa") diff --git a/contest/migrations/0001_initial.py b/contest/migrations/0001_initial.py new file mode 100644 index 00000000..01df2877 --- /dev/null +++ b/contest/migrations/0001_initial.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('group', '0004_merge'), + ] + + operations = [ + migrations.CreateModel( + name='Contest', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('title', models.CharField(unique=True, max_length=40)), + ('description', models.TextField()), + ('mode', models.IntegerField()), + ('show_rank', models.BooleanField()), + ('show_user_submission', models.BooleanField()), + ('password', models.CharField(max_length=30, null=True, blank=True)), + ('contest_type', models.IntegerField()), + ('start_time', models.DateTimeField()), + ('end_time', models.DateTimeField()), + ('create_time', models.DateTimeField(auto_now_add=True)), + ('last_updated_time', models.DateTimeField(auto_now=True)), + ('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('groups', models.ManyToManyField(to='group.Group')), + ], + options={ + 'db_table': 'contest', + }, + ), + migrations.CreateModel( + name='ContestProblem', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('title', models.CharField(max_length=50)), + ('description', models.TextField()), + ('input_description', models.CharField(max_length=10000)), + ('output_description', models.CharField(max_length=10000)), + ('samples', models.TextField(blank=True)), + ('test_case_id', models.CharField(max_length=40)), + ('hint', models.TextField(null=True, blank=True)), + ('create_time', models.DateTimeField(auto_now_add=True)), + ('time_limit', models.IntegerField()), + ('memory_limit', models.IntegerField()), + ('visible', models.BooleanField(default=True)), + ('total_submit_number', models.IntegerField(default=0)), + ('total_accepted_number', models.IntegerField(default=0)), + ('sort_index', models.CharField(max_length=30)), + ('contest', models.ForeignKey(to='contest.Contest')), + ('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'contest_problem', + }, + ), + migrations.CreateModel( + name='ContestProblemTestCase', + fields=[ + ('id', models.CharField(max_length=40, serialize=False, primary_key=True, db_index=True)), + ('score', models.IntegerField()), + ('problem', models.ForeignKey(to='contest.ContestProblem')), + ], + options={ + 'db_table': 'contest_problem_test_case', + }, + ), + ] diff --git a/contest/migrations/0002_contest_visible.py b/contest/migrations/0002_contest_visible.py new file mode 100644 index 00000000..7ee207d7 --- /dev/null +++ b/contest/migrations/0002_contest_visible.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', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='contest', + name='visible', + field=models.BooleanField(default=True), + ), + ] diff --git a/contest/models.py b/contest/models.py index 838d6d90..0ece5fff 100644 --- a/contest/models.py +++ b/contest/models.py @@ -33,6 +33,8 @@ class Contest(models.Model): # 这个比赛是谁创建的 created_by = models.ForeignKey(User) groups = models.ManyToManyField(Group) + # 是否可见 false的话相当于删除 + visible = models.BooleanField(default=True) class Meta: db_table = "contest" diff --git a/contest/serializers.py b/contest/serializers.py index 51a4066a..3f791912 100644 --- a/contest/serializers.py +++ b/contest/serializers.py @@ -13,10 +13,11 @@ class CreateContestSerializer(serializers.Serializer): contest_type = serializers.IntegerField() show_rank = serializers.BooleanField() show_user_submission = serializers.BooleanField() - password = serializers.CharField(max_length=30, required=False) + password = serializers.CharField(max_length=30, required=False, default=None) start_time = serializers.DateTimeField() end_time = serializers.DateTimeField() - groups = serializers.ListField(child=serializers.IntegerField()) + groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[]) + visible = serializers.BooleanField() class ContestSerializer(serializers.ModelSerializer): @@ -32,16 +33,18 @@ class ContestSerializer(serializers.ModelSerializer): class EditContestSerializer(serializers.Serializer): + id = serializers.IntegerField() title = serializers.CharField(max_length=40) description = serializers.CharField(max_length=10000) mode = serializers.IntegerField() contest_type = serializers.IntegerField() show_rank = serializers.BooleanField() show_user_submission = serializers.BooleanField() - password = serializers.CharField(max_length=30, required=False) + password = serializers.CharField(max_length=30, required=False, default=None) start_time = serializers.DateTimeField() end_time = serializers.DateTimeField() - groups = serializers.ListField(child=serializers.IntegerField()) + groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[]) + visible = serializers.BooleanField() class ContestProblemSampleSerializer(serializers.ListField): diff --git a/contest/tests.py b/contest/tests.py index 7ce503c2..01be7f84 100644 --- a/contest/tests.py +++ b/contest/tests.py @@ -1,3 +1,219 @@ +# coding=utf-8 +import json +from django.core.urlresolvers import reverse from django.test import TestCase -# Create your tests here. +from rest_framework.test import APITestCase, APIClient + +from account.models import User +from group.models import Group +from contest.models import Contest, ContestProblem +from announcement.models import Announcement +from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN + + +class ContestAdminAPITest(APITestCase): + def setUp(self): + self.client = APIClient() + self.url = reverse('contest_admin_api') + user1 = User.objects.create(username="test1", admin_type=SUPER_ADMIN) + user1.set_password("testaa") + user1.save() + user2 = User.objects.create(username="test2", admin_type=ADMIN) + user2.set_password("testbb") + user2.save() + user3 = User.objects.create(username="test3", admin_type=REGULAR_USER) + user3.set_password("testcc") + user3.save() + self.group = Group.objects.create(name="group1", description="des0", + join_group_setting=0, visible=True, + admin=user2) + 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.group_contest = Contest.objects.create(title="titley", description="descriptiony", 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")) + + # 以下是比赛发布的测试 + def test_invalid_format(self): + self.client.login(username="test1", password="testaa") + data = {"title": "test1"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 1) + + def test_global_contest_does_not_has_privileges(self): + self.client.login(username="test2", password="testbb") + data = {"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", "password": "aabb", "visible": True} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"只有超级管理员才可创建公开赛"}) + + def test_global_contest_password_exists(self): + self.client.login(username="test1", password="testaa") + data = {"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.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"此比赛为有密码的公开赛,密码不可为空"}) + + def test_group_contest_group_at_least_one(self): + self.client.login(username="test1", password="testaa") + data = {"title": "title0", "description": "description0", "mode": 1, "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", "visible": True} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"请至少选择一个小组"}) + + def test_global_contest_successfully(self): + self.client.login(username="test1", password="testaa") + data = {"title": "title1", "description": "description1", "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": "aabb", "visible": True} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 0) + + def test_group_contest_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", + "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, + "show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T12:00:00.000Z", + "end_time": "2015-08-15T10:00:00.000Z", "password": "aabb", "visible": True} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"比赛的开始时间不能晚于或等于比赛结束的时间"}) + + def test_contest_has_exists(self): + self.client.login(username="test1", password="testaa") + data = {"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": "aabb", "visible": True} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"比赛名已经存在"}) + + # 以下是编辑比赛的测试 + def test_put_invalid_data(self): + self.client.login(username="test1", password="testaa") + data = {"title": "test0", "description": "description0"} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 1) + + def test_contest_does_not_exist(self): + self.client.login(username="test1", password="testaa") + data = {"id": self.global_contest.id + 10, "title": "title2", "description": "description2", "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": "aabb", + "visible": True} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"该比赛不存在!"}) + + def test_edit_global_contest_successfully(self): + self.client.login(username="test1", password="testaa") + data = {"id": self.global_contest.id, "title": "titlez", "description": "descriptionz", "mode": 1, + "contest_type": 2, "show_rank": True, "show_user_submission": True, + "start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T13:00:00.000Z", "password": "aabb", + "visible": True} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 0) + self.assertEqual(response.data["data"]["title"], "titlez") + self.assertEqual(response.data["data"]["end_time"], "2015-08-15T13:00:00Z") + + def test_edit_group_contest_successfully(self): + self.client.login(username="test1", password="testaa") + 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"], 0) + self.assertEqual(response.data["data"]["title"], "titleyyy") + self.assertEqual(response.data["data"]["end_time"], "2015-08-15T13:00:00Z") + self.assertEqual(response.data["data"]["visible"], False) + + 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, + "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", "visible": True} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"请至少选择一个小组"}) + + def test_edit_contest_has_exists(self): + self.client.login(username="test1", password="testaa") + data = {"id": self.global_contest.id, "title": "titley", "description": "descriptiony", "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": "aabb", + "visible": True} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"该比赛名称已经存在"}) + + def test_edit_global_contest_does_not_has_privileges(self): + self.client.login(username="test2", password="testbb") + data = {"id": self.global_contest.id, "title": "titlexxxxxxxxx", "description": "descriptionxxxxxx", "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": "aabb", + "visible": True} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"只有超级管理员才可创建公开赛"}) + + 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, + "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) + self.assertEqual(response.data, {"code": 1, "data": u"此比赛为有密码的公开赛,密码不可为空"}) + + def test_edit_time_error(self): + self.client.login(username="test1", password="testaa") + data = {"id": self.global_contest.id, "title": "titleaaaa", "description": "descriptionaaaaa", "mode": 1, + "contest_type": 2, "show_rank": True, "show_user_submission": True, + "start_time": "2015-08-15T12:00:00.000Z", "end_time": "2015-08-15T10:00:00.000Z", "password": "aabb", + "visible": True} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"比赛的开始时间不能晚于或等于比赛结束的时间"}) + + # 以下是比赛分页的测试 + def test_get_data_successfully(self): + self.client.login(username="test1", password="testaa") + 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") + self.assertEqual(response.data["code"], 0) + for item in response.data["data"]: + self.assertEqual(item["visible"], True) + + +class ContestProblemAdminAPItEST(APITestCase): + def setUp(self): + self.client = APIClient() + self.url = reverse('contest_problem_admin_api') + self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN) + self.user.set_password("testaa") + self.user.save() + self.client.login(username="test", password="testaa") + self. contest_problem = ContestProblem.objects.create(title="title1", + description="description1", + 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, + difficulty=1, + hint="hint1", + created_by=User.objects.get(username="test"), + sort_index="a") diff --git a/contest/views.py b/contest/views.py index 31ed0f44..338a4893 100644 --- a/contest/views.py +++ b/contest/views.py @@ -2,6 +2,7 @@ import json from django.shortcuts import render from django.db import IntegrityError +from django.utils import dateparse from django.db.models import Q from rest_framework.views import APIView from utils.shortcuts import (serializer_invalid_response, error_response, @@ -15,7 +16,6 @@ from .serializers import (CreateContestSerializer, ContestSerializer, EditContes CreateContestProblemSerializer, ContestProblemSerializer, EditContestProblemSerializer) - def contest_page(request, contest_id): pass @@ -49,12 +49,16 @@ class ContestAdminAPIView(APIView): groups = Group.objects.filter(id__in=data["groups"], admin=request.user) if not groups.count(): return error_response(u"请至少选择一个小组") + if data["start_time"] >= data["end_time"]: + return error_response(u"比赛的开始时间不能晚于或等于比赛结束的时间") try: contest = Contest.objects.create(title=data["title"], description=data["description"], - mode=data["mode"], show_rank=data["show_rank"], + mode=data["mode"], contest_type=data["contest_type"], + show_rank=data["show_rank"], password=data["password"], show_user_submission=data["show_user_submission"], - start_time=["start_time"], end_time=data["end_time"], - password=data["password"]) + start_time=dateparse.parse_datetime(data["start_time"]), + end_time=dateparse.parse_datetime(data["end_time"]), + created_by=request.user, visible=data["visible"]) except IntegrityError: return error_response(u"比赛名已经存在") contest.groups.add(*groups) @@ -96,13 +100,17 @@ class ContestAdminAPIView(APIView): groups = Group.objects.filter(id__in=data["groups"], admin=request.user) if not groups.count(): return error_response(u"请至少选择一个小组") + if data["start_time"] >= data["end_time"]: + return error_response(u"比赛的开始时间不能晚于或等于比赛结束的时间") contest.title = data["title"] contest.description = data["description"] contest.mode = data["mode"] + contest.contest_type = data["contest_type"] contest.show_rank = data["show_rank"] contest.show_user_submission = data["show_user_submission"] - contest.start_time = ["start_time"] - contest.end_time = data["end_time"] + contest.start_time = dateparse.parse_datetime(data["start_time"]) + contest.end_time = dateparse.parse_datetime(data["end_time"]) + contest.visible = data["visible"] contest.password = data["password"] contest.save() diff --git a/group/views.py b/group/views.py index a9ec8d24..ff1cbde4 100644 --- a/group/views.py +++ b/group/views.py @@ -226,7 +226,8 @@ class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase): if serializer.is_valid(): data = serializer.data try: - join_request = JoinGroupRequest.objects.get(id=data["request_id"], group__admin=request.user, status=False) + join_request = JoinGroupRequest.objects.get(id=data["request_id"], group__admin=request.user, + status=False) except JoinGroupRequest.DoesNotExist: return error_response(u"请求不存在") diff --git a/oj/settings.py b/oj/settings.py index f956f5bc..472d77ea 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -52,6 +52,7 @@ INSTALLED_APPS = ( 'problem', 'admin', 'submission', + 'contest', 'rest_framework', 'rest_framework_swagger', diff --git a/oj/urls.py b/oj/urls.py index ef9e5032..0d3afa55 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -8,6 +8,8 @@ from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterA UserAdminAPIView, UserInfoAPIView) from announcement.views import AnnouncementAdminAPIView +from contest.views import ContestAdminAPIView, ContestAdminAPIView + from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView, JoinGroupAPIView, JoinGroupRequestAdminAPIView) @@ -34,6 +36,7 @@ urlpatterns = [ url(r'^api/username_check/$', UsernameCheckAPIView.as_view(), name="username_check_api"), url(r'^api/email_check/$', EmailCheckAPIView.as_view(), name="email_check_api"), 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"), url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), url(r'^announcement/(?P\d+)/$', "announcement.views.announcement_page", @@ -48,6 +51,7 @@ urlpatterns = [ url(r'^api/admin/group_member/$', GroupMemberAdminAPIView.as_view(), name="group_member_admin_api"), url(r'^api/admin/group_join/$', JoinGroupAPIView.as_view(), name="group_join_admin_api"), url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"), + url(r'^api/admin/contest_problem/$', ContestAdminAPIView.as_view(), name="contest_problem_admin_api"), url(r'^api/admin/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_api"), url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"), url(r'^problem/(?P\d+)/my_submissions/$', "submission.views.problem_my_submissions_list_page", diff --git a/problem/tests.py b/problem/tests.py index 925ffe08..ab6d8985 100644 --- a/problem/tests.py +++ b/problem/tests.py @@ -1,7 +1,7 @@ # coding=utf-8 import json -from django.test import TestCase +from django.test import TestCase, Client from django.core.urlresolvers import reverse from rest_framework.test import APITestCase, APIClient @@ -39,7 +39,6 @@ class ProblemPageTest(TestCase): self.assertTemplateUsed(response, "utils/error.html") - class ProblemAdminTest(APITestCase): def _create_data(self, problem_id, visible, tags): data = {"id": problem_id, @@ -167,5 +166,26 @@ class ProblemTagAdminAPITest(APITestCase): class ProblemListPageTest(TestCase): def setUp(self): - pass + self.client = Client() + self.url = reverse('problem_list_page', kwargs={"page": 1}) + self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN) + self.user.set_password("testaa") + self.user.save() + self.client.login(username="test", password="testaa") + ProblemTag.objects.create(name="tag1") + ProblemTag.objects.create(name="tag2") + self.problem = Problem.objects.create(title="title1", + description="description1", + input_description="input1_description", + output_description="output1_description", + test_case_id="1", + source="source1", + samples=json.dumps([{"input": "1 1", "output": "2"}]), + time_limit=100, + memory_limit=1000, + difficulty=1, + hint="hint1", + created_by=User.objects.get(username="test")) + + diff --git a/problem/views.py b/problem/views.py index 4a6e2c62..6ae22800 100644 --- a/problem/views.py +++ b/problem/views.py @@ -56,7 +56,6 @@ class ProblemAdminAPIView(APIView): difficulty=data["difficulty"], created_by=request.user, hint=data["hint"]) - for tag in data["tags"]: try: tag = ProblemTag.objects.get(name=tag) From 8b4580d8f608542b475bccb3cc025c15be6203b4 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Sat, 22 Aug 2015 14:00:09 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E6=AF=94=E8=B5=9B?= =?UTF-8?q?=E4=B8=8E=E6=AF=94=E8=B5=9B=E9=A2=98=E7=9B=AE=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=B7=B2=E4=BF=AE=E6=94=B9=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- announcement/views.py | 4 +- .../0003_contestproblem_difficulty.py | 20 +++ .../0004_remove_contestproblem_difficulty.py | 18 +++ contest/serializers.py | 11 +- contest/tests.py | 129 +++++++++++++++++- contest/views.py | 14 +- oj/urls.py | 4 +- 7 files changed, 179 insertions(+), 21 deletions(-) create mode 100644 contest/migrations/0003_contestproblem_difficulty.py create mode 100644 contest/migrations/0004_remove_contestproblem_difficulty.py diff --git a/announcement/views.py b/announcement/views.py index e49b1b4a..818aa0a9 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -36,7 +36,7 @@ class AnnouncementAdminAPIView(APIView): if request.user.admin_type == SUPER_ADMIN: groups = Group.objects.filter(id__in=data["groups"]) else: - groups = Group.objects.filter(id__in=data["groups"], created_by=request.user) + groups = Group.objects.filter(id__in=data["groups"], admin=request.user) if not groups.count(): return error_response(u"至少选择一个小组") else: @@ -75,7 +75,7 @@ class AnnouncementAdminAPIView(APIView): if request.user.admin_type == SUPER_ADMIN: groups = Group.objects.filter(id__in=data["groups"]) else: - groups = Group.objects.filter(id__in=data["groups"], created_by=request.user) + groups = Group.objects.filter(id__in=data["groups"], admin=request.user) if not groups.count(): return error_response(u"至少选择一个小组") announcement.title = data["title"] diff --git a/contest/migrations/0003_contestproblem_difficulty.py b/contest/migrations/0003_contestproblem_difficulty.py new file mode 100644 index 00000000..9d33b591 --- /dev/null +++ b/contest/migrations/0003_contestproblem_difficulty.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('contest', '0002_contest_visible'), + ] + + operations = [ + migrations.AddField( + model_name='contestproblem', + name='difficulty', + field=models.IntegerField(default=1), + preserve_default=False, + ), + ] diff --git a/contest/migrations/0004_remove_contestproblem_difficulty.py b/contest/migrations/0004_remove_contestproblem_difficulty.py new file mode 100644 index 00000000..41ee147b --- /dev/null +++ b/contest/migrations/0004_remove_contestproblem_difficulty.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('contest', '0003_contestproblem_difficulty'), + ] + + operations = [ + migrations.RemoveField( + model_name='contestproblem', + name='difficulty', + ), + ] diff --git a/contest/serializers.py b/contest/serializers.py index 3f791912..dd9f3709 100644 --- a/contest/serializers.py +++ b/contest/serializers.py @@ -3,6 +3,7 @@ import json from rest_framework import serializers from account.models import User +from account.serializers import UserSerializer from .models import Contest, ContestProblem @@ -58,6 +59,7 @@ class JSONField(serializers.Field): class CreateContestProblemSerializer(serializers.Serializer): + contest_id = serializers.IntegerField() title = serializers.CharField(max_length=50) description = serializers.CharField(max_length=10000) input_description = serializers.CharField(max_length=10000) @@ -67,20 +69,20 @@ class CreateContestProblemSerializer(serializers.Serializer): test_case_id = serializers.CharField(max_length=40) time_limit = serializers.IntegerField() memory_limit = serializers.IntegerField() - difficulty = serializers.IntegerField() hint = serializers.CharField(max_length=3000, allow_blank=True) sort_index = serializers.CharField(max_length=30) class ContestProblemSerializer(serializers.ModelSerializer): - samples = JSONField() class ContestSerializer(serializers.ModelSerializer): class Meta: model = Contest - fields = ["title"] + fields = ["title", "id"] - created_by = ContestSerializer() + samples = JSONField() + contest = ContestSerializer() + created_by = UserSerializer() class Meta: model = ContestProblem @@ -95,7 +97,6 @@ class EditContestProblemSerializer(serializers.Serializer): test_case_id = serializers.CharField(max_length=40) time_limit = serializers.IntegerField() memory_limit = serializers.IntegerField() - difficulty = serializers.IntegerField() samples = ContestProblemSampleSerializer() hint = serializers.CharField(max_length=3000, allow_blank=True) visible = serializers.BooleanField() diff --git a/contest/tests.py b/contest/tests.py index 01be7f84..6cce9e85 100644 --- a/contest/tests.py +++ b/contest/tests.py @@ -196,24 +196,141 @@ class ContestAdminAPITest(APITestCase): for item in response.data["data"]: self.assertEqual(item["visible"], True) + def test_query_by_keyword(self): + self.client.login(username="test1", password="testaa") + response = self.client.get(self.url + "?keyword=title1") + self.assertEqual(response.data["code"], 0) + class ContestProblemAdminAPItEST(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse('contest_problem_admin_api') - self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN) + self.user = User.objects.create(username="test1", admin_type=SUPER_ADMIN) self.user.set_password("testaa") self.user.save() - self.client.login(username="test", password="testaa") - self. contest_problem = ContestProblem.objects.create(title="title1", - description="description1", + 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, - difficulty=1, hint="hint1", - created_by=User.objects.get(username="test"), + created_by=User.objects.get(username="test1"), + contest=Contest.objects.get(title="titlex"), sort_index="a") + + # 以下是发布比赛题目的测试 + def test_invalid_format(self): + data = {"title": "test1"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 1) + + def test_release_contest_problem_successfully(self): + data = {"title": "title2", + "description": "description2", + "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", + "contest_id": self.global_contest.id} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 0) + + def test_contest_does_not_exists(self): + data = {"title": "titlezzzzzzzz", + "description": "descriptionzzzzzzzzzzz", + "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", + "contest_id": self.global_contest.id + 10} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"比赛不存在"}) + + # 以下是编辑比赛题目的测试 + def test_invalid_data(self): + data = {"title": "test1"} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 1) + + def test_edit_problem_does_not_exist(self): + data = {"id": self.contest_problem.id + 1, + "title": "title2", + "description": "description2", + "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, "data": u"该比赛题目不存在!"}) + + def test_edit_problem_successfully(self): + 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"], 0) + + # 以下是比赛题目分页的测试 + def test_get_data_successfully(self): + self.client.login(username="test1", password="testaa") + 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") + self.assertEqual(response.data["code"], 0) + for item in response.data["data"]: + self.assertEqual(item["visible"], True) + + def test_query_by_keyword(self): + self.client.login(username="test1", password="testaa") + response = self.client.get(self.url + "?keyword=title1") + self.assertEqual(response.data["code"], 0) + + def test_query_contest_problem_does_not_exist(self): + data = {"contest_problem_id": 1000000} + response = self.client.get(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"比赛题目不存在"}) + + def test_query_contest_problem_exists(self): + data = {"contest_problem_id": self.contest_problem.id} + response = self.client.get(self.url, data=data) + self.assertEqual(response.data["code"], 0) + + diff --git a/contest/views.py b/contest/views.py index 338a4893..d00bee5a 100644 --- a/contest/views.py +++ b/contest/views.py @@ -148,6 +148,10 @@ class ContestProblemAdminAPIView(APIView): serializer = CreateContestProblemSerializer(data=request.data) if serializer.is_valid(): data = serializer.data + try: + contest = Contest.objects.get(id=data["contest_id"]) + except Contest.DoesNotExist: + return error_response(u"比赛不存在") contest_problem = ContestProblem.objects.create(title=data["title"], description=data["description"], input_description=data["input_description"], @@ -156,10 +160,9 @@ class ContestProblemAdminAPIView(APIView): samples=json.dumps(data["samples"]), time_limit=data["time_limit"], memory_limit=data["memory_limit"], - difficulty=data["difficulty"], created_by=request.user, hint=data["hint"], - contest=request.contest, + contest=contest, sort_index=data["sort_index"]) return success_response(ContestProblemSerializer(contest_problem).data) else: @@ -169,10 +172,10 @@ class ContestProblemAdminAPIView(APIView): """ 比赛题目编辑json api接口 --- - request_serializer: EditContestSerializer + request_serializer: EditContestProblemSerializer response_serializer: ContestProblemSerializer """ - serializer = EditContestSerializer(data=request.data) + serializer = EditContestProblemSerializer(data=request.data) if serializer.is_valid(): data = serializer.data try: @@ -186,13 +189,12 @@ class ContestProblemAdminAPIView(APIView): contest_problem.test_case_id = data["test_case_id"] contest_problem.time_limit = data["time_limit"] contest_problem.memory_limit = data["memory_limit"] - contest_problem.difficulty = data["difficulty"] contest_problem.samples = json.dumps(data["samples"]) contest_problem.hint = data["hint"] contest_problem.visible = data["visible"] contest_problem.sort_index = data["sort_index"] contest_problem.save() - return success_response(ContestProblem(contest_problem).data) + return success_response(ContestProblemSerializer(contest_problem).data) else: return serializer_invalid_response(serializer) diff --git a/oj/urls.py b/oj/urls.py index 3cfcd90f..6dc76064 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -8,7 +8,7 @@ from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterA UserAdminAPIView, UserInfoAPIView) from announcement.views import AnnouncementAdminAPIView -from contest.views import ContestAdminAPIView, ContestAdminAPIView +from contest.views import ContestAdminAPIView, ContestProblemAdminAPIView from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView, JoinGroupAPIView, JoinGroupRequestAdminAPIView) @@ -52,7 +52,7 @@ urlpatterns = [ url(r'^api/admin/group_member/$', GroupMemberAdminAPIView.as_view(), name="group_member_admin_api"), url(r'^api/admin/group_join/$', JoinGroupAPIView.as_view(), name="group_join_admin_api"), url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"), - url(r'^api/admin/contest_problem/$', ContestAdminAPIView.as_view(), name="contest_problem_admin_api"), + url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"), url(r'^api/admin/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_api"), url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"), url(r'^problem/(?P\d+)/my_submissions/$', "submission.views.problem_my_submissions_list_page",