From fb038cfc8ed6a864a9068975a8fa83907ac08a51 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Sun, 9 Aug 2015 20:03:22 +0800 Subject: [PATCH 1/6] =?UTF-8?q?[=E5=90=8E=E5=8F=B0=E9=A2=98=E7=9B=AE?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=9A=84api=E9=83=A8=E5=88=86]=20=20?= =?UTF-8?q?=E5=A2=9E=E6=B7=BB=E5=88=9B=E5=BB=BA=E9=A2=98=E7=9B=AE=E3=80=81?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E9=A2=98=E7=9B=AEAPI=E5=8F=8A=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/settings.py | 1 + oj/urls.py | 3 ++ problem/models.py | 10 ++--- problem/serizalizers.py | 62 +++++++++++++++++++++++++++ problem/tests.py | 77 ++++++++++++++++++++++++++++++++++ problem/views.py | 92 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 239 insertions(+), 6 deletions(-) create mode 100644 problem/serizalizers.py diff --git a/oj/settings.py b/oj/settings.py index c8a07ec..6e01c10 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -50,6 +50,7 @@ INSTALLED_APPS = ( 'announcement', 'utils', 'group', + 'problem', 'rest_framework', 'rest_framework_swagger', diff --git a/oj/urls.py b/oj/urls.py index b2eecb9..9e4f08d 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -9,6 +9,7 @@ from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterA from announcement.views import AnnouncementAPIView, AnnouncementAdminAPIView from group.views import GroupAdminAPIView from admin.views import AdminTemplateView +from problem.views import ProblemAdminAPIView urlpatterns = [ url("^$", TemplateView.as_view(template_name="oj/index.html"), name="index_page"), @@ -33,4 +34,6 @@ urlpatterns = [ url(r'^problems/$', TemplateView.as_view(template_name="oj/problem/problem_list.html"), name="problem_list_page"), url(r'^admin/template/(?P\w+)/(?P\w+).html', AdminTemplateView.as_view(), name="admin_template"), url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"), + + url(r'api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"), ] diff --git a/problem/models.py b/problem/models.py index e8255cd..0132072 100644 --- a/problem/models.py +++ b/problem/models.py @@ -18,21 +18,19 @@ class AbstractProblem(models.Model): # 问题描述 HTML 格式 description = models.TextField() # 样例输入 可能会存储 json 格式的数据 - sample_input = models.TextField(blank=True) - # 样例输出 同上 - sample_output = models.TextField(blank=True) + sample = models.TextField(blank=True) # 测试用例id 这个id 可以用来拼接得到测试用例的文件存储位置 test_case_id = models.CharField(max_length=40) # 提示 - hint = models.TextField(blank=True) + hint = models.TextField(blank=True, null=True) # 创建时间 - create_time = models.DateTimeField(auth_now_add=True) + create_time = models.DateTimeField(auto_now_add=True) # 最后更新时间 last_update_time = models.DateTimeField(auto_now=True) # 这个题是谁创建的 created_by = models.ForeignKey(User) # 来源 - source = models.CharField(max_length=30, blank=True) + source = models.CharField(max_length=30, blank=True, null=True) # 时间限制 单位是毫秒 time_limit = models.IntegerField() # 内存限制 单位是MB diff --git a/problem/serizalizers.py b/problem/serizalizers.py new file mode 100644 index 0000000..441c9c5 --- /dev/null +++ b/problem/serizalizers.py @@ -0,0 +1,62 @@ +# coding=utf-8 +import json + +from rest_framework import serializers + +from account.models import User +from .models import Problem + + +class ProblemSampleSerializer(serializers.ListField): + input = serializers.CharField(max_length=3000) + output = serializers.CharField(max_length=3000) + + +class JSONField(serializers.Field): + def to_representation(self, value): + print value, type(value) + return json.loads(value) + + +class CreateProblemSerializer(serializers.Serializer): + title = serializers.CharField(max_length=50) + description = serializers.CharField(max_length=10000) + # [{"input": "1 1", "output": "2"}] + sample = ProblemSampleSerializer() + test_case_id = serializers.CharField(max_length=40) + source = serializers.CharField(max_length=30, required=False, default=None) + time_limit = serializers.IntegerField() + memory_limit = serializers.IntegerField() + difficulty = serializers.IntegerField() + tags = serializers.ListField(child=serializers.IntegerField()) + hint = serializers.CharField(max_length=3000, required=False, default=None) + + +class ProblemSerializer(serializers.ModelSerializer): + sample = JSONField() + + class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ["username"] + + created_by = UserSerializer() + + class Meta: + model = Problem + + +class EditProblemSerializer(serializers.Serializer): + id = serializers.IntegerField() + title = serializers.CharField(max_length=50) + description = serializers.CharField(max_length=10000) + test_case_id = serializers.CharField(max_length=40) + source = serializers.CharField(max_length=30) + time_limit = serializers.IntegerField() + memory_limit = serializers.IntegerField() + difficulty = serializers.IntegerField() + tags = serializers.ListField(child=serializers.IntegerField()) + sample = ProblemSampleSerializer() + hint = serializers.CharField(max_length=10000) + visible = serializers.BooleanField() + diff --git a/problem/tests.py b/problem/tests.py index b66a494..9fb0406 100644 --- a/problem/tests.py +++ b/problem/tests.py @@ -1,6 +1,83 @@ # coding=utf-8 from django.test import TestCase +from django.core.urlresolvers import reverse +from rest_framework.test import APITestCase, APIClient + +from account.models import User, SUPER_ADMIN +from problem.models import Problem, ProblemTag + class ProblemPageTest(TestCase): pass + + +class ProblemAdminTest(APITestCase): + def setUp(self): + self.client = APIClient() + self.url = reverse("problem_admin_api") + user = User.objects.create(username="test", admin_type=SUPER_ADMIN) + user.set_password("testaa") + user.save() + + # 以下是发布题目的测试 + def test_invalid_format(self): + self.client.login(username="test", password="testaa") + data = {"title": "test1"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 1) + + def test_success_problem(self): + self.client.login(username="test", password="testaa") + ProblemTag.objects.create(name="tag1", description="destag1") + data = {"title": "title1", "description": "des1", "test_case_id": "1", "source": "source1", + "sample": [{"input": "1 1", "output": "2"}], "time_limit": "100", "memory_limit": "1000", + "difficulty": "1", "hint": "hint1", "tags": [1]} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 0) + + # 以下是编辑题目的测试 + def test_put_invalid_data(self): + self.client.login(username="test", password="testaa") + data = {"title": "test0"} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 1) + + def test_problem_does_not_exist(self): + self.client.login(username="test", password="testaa") + ProblemTag.objects.create(name="tag1", description="destag1") + tags = ProblemTag.objects.filter(id__in=[1]) + problem = Problem.objects.create(title="title1", description="des1", + test_case_id="1", source="source1", + sample=[{"input": "1 1", "output": "2"}], + time_limit=100, memory_limit=1000, + difficulty=1, hint="hint1", + created_by=User.objects.get(username="test")) + problem.tags.add(*tags) + data = {"id": 2, "title": "title1", "description": "des1", "test_case_id": "1", "source": "source1", + "sample": [{"input": "1 1", "output": "2"}], "time_limit": "100", "memory_limit": "1000", + "difficulty": "1", "hint": "hint1", "tags": [1]} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"该题目不存在!"}) + + def test_success_edit_problem(self): + self.client.login(username="test", password="testaa") + self.client.login(username="test", password="testaa") + ProblemTag.objects.create(name="tag1", description="destag1") + ProblemTag.objects.create(name="tag2", description="destag2") + tags = ProblemTag.objects.filter(id__in=[1]) + problem0 = Problem.objects.create(title="title1", description="des1", + test_case_id="1", source="source1", + sample=[{"input": "1 1", "output": "2"}], + time_limit=100, memory_limit=1000, + difficulty=1, hint="hint1", + created_by=User.objects.get(username="test")) + problem0.tags.add(*tags) + data = {"id": 1, "title": "title1", "description": "des1", "test_case_id": "1", "source": "source1", + "sample": [{"input": "1 1", "output": "2"}], "time_limit": "100", "memory_limit": "1000", + "difficulty": "1", "hint": "hint1", "visible": True, "tags": [1, 2]} + problem = Problem.objects.get(id=data["id"]) + problem.tags.remove(*problem.tags.all()) + problem.tags.add(*ProblemTag.objects.filter(id__in=data["tags"])) + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 0) diff --git a/problem/views.py b/problem/views.py index 58fb9e6..cf45d93 100644 --- a/problem/views.py +++ b/problem/views.py @@ -1,7 +1,99 @@ # coding=utf-8 +import json from django.shortcuts import render +from rest_framework.views import APIView + +from django.db.models import Q + +from serizalizers import CreateProblemSerializer, EditProblemSerializer, ProblemSerializer +from .models import Problem, ProblemTag +from utils.shortcuts import serializer_invalid_response, error_response, success_response, paginate + def problem_page(request, problem_id): # todo return render(request, "oj/problem/problem.html") + + +class ProblemAdminAPIView(APIView): + def post(self, request): + """ + 题目发布json api接口 + --- + request_serializer: CreateProblemSerializer + response_serializer: ProblemSerializer + """ + serializer = CreateProblemSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + problem = Problem.objects.create(title=data["title"], + description=data["description"], + test_case_id=data["test_case_id"], + source=data["source"], + sample=json.dumps(data["sample"]), + time_limit=data["time_limit"], + memory_limit=data["memory_limit"], + difficulty=data["difficulty"], + created_by=request.user, + hint=data["hint"]) + + tags = ProblemTag.objects.filter(id__in=data["tags"]) + problem.tags.add(*tags) + return success_response(ProblemSerializer(problem).data) + else: + return serializer_invalid_response(serializer) + + def put(self, request): + """ + 题目编辑json api接口 + --- + request_serializer: EditProblemSerializer + response_serializer: ProblemSerializer + """ + serializer = EditProblemSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + print request.data + try: + problem = Problem.objects.get(id=data["id"]) + except Problem.DoesNotExist: + return error_response(u"该题目不存在!") + + problem.title = data["title"] + problem.description = data["description"] + problem.test_case_id = data["test_case_id"] + problem.source = data["source"] + problem.time_limit = data["time_limit"] + problem.memory_limit = data["memory_limit"] + problem.difficulty = data["difficulty"] + problem.sample = json.dumps(data["sample"]) + problem.hint = data["hint"] + problem.visible = data["visible"] + + # 删除原有的标签的对应关系 + problem.tags.remove(*problem.tags.all()) + # 重新添加所有的标签 + problem.tags.add(*ProblemTag.objects.filter(id__in=data["tags"])) + problem.save() + return success_response(ProblemSerializer(problem).data) + else: + return serializer_invalid_response(serializer) + + +class ProblemAPIView(APIView): + def get(self, request): + """ + 题目分页json api接口 + --- + response_serializer: ProblemSerializer + """ + problem = Problem.objects.all().order_by("-last_update_time") + visible = request.GET.get("visible", None) + if visible: + problem = problem.filter(visible=(visible == "true")) + keyword = request.GET.get("keyword", None) + if keyword: + problem = problem.filter(Q(difficulty__contains=keyword)) + + return paginate(request, problem, ProblemSerializer) From c9c9f1d15048887be06d8306f706ca6723fd7f82 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Sun, 9 Aug 2015 20:58:08 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9problem=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=B8=ADdata=E5=8F=98=E9=87=8F=E4=BB=A5=E5=8F=8A=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E5=88=9B=E5=BB=BA=E4=B8=AD=E5=90=84=E4=B8=AA=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E4=B9=8B=E9=97=B4=E7=9A=84=E6=8D=A2=E8=A1=8C=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problem/tests.py | 66 +++++++++++++++++++++++++++++++++++------------- problem/views.py | 2 +- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/problem/tests.py b/problem/tests.py index 9fb0406..5bf89ba 100644 --- a/problem/tests.py +++ b/problem/tests.py @@ -30,9 +30,16 @@ class ProblemAdminTest(APITestCase): def test_success_problem(self): self.client.login(username="test", password="testaa") ProblemTag.objects.create(name="tag1", description="destag1") - data = {"title": "title1", "description": "des1", "test_case_id": "1", "source": "source1", - "sample": [{"input": "1 1", "output": "2"}], "time_limit": "100", "memory_limit": "1000", - "difficulty": "1", "hint": "hint1", "tags": [1]} + data = {"title": "title1", + "description": "des1", + "test_case_id": "1", + "source": "source1", + "sample": [{"input": "1 1", "output": "2"}], + "time_limit": "100", + "memory_limit": "1000", + "difficulty": "1", + "hint": "hint1", + "tags": [1]} response = self.client.post(self.url, data=data) self.assertEqual(response.data["code"], 0) @@ -47,16 +54,28 @@ class ProblemAdminTest(APITestCase): self.client.login(username="test", password="testaa") ProblemTag.objects.create(name="tag1", description="destag1") tags = ProblemTag.objects.filter(id__in=[1]) - problem = Problem.objects.create(title="title1", description="des1", - test_case_id="1", source="source1", + problem = Problem.objects.create(title="title1", + description="des1", + test_case_id="1", + source="source1", sample=[{"input": "1 1", "output": "2"}], - time_limit=100, memory_limit=1000, - difficulty=1, hint="hint1", + time_limit=100, + memory_limit=1000, + difficulty=1, + hint="hint1", created_by=User.objects.get(username="test")) problem.tags.add(*tags) - data = {"id": 2, "title": "title1", "description": "des1", "test_case_id": "1", "source": "source1", - "sample": [{"input": "1 1", "output": "2"}], "time_limit": "100", "memory_limit": "1000", - "difficulty": "1", "hint": "hint1", "tags": [1]} + data = {"id": 2, + "title": "title1", + "description": "des1", + "test_case_id": "1", + "source": "source1", + "sample": [{"input": "1 1", "output": "2"}], + "time_limit": "100", + "memory_limit": "1000", + "difficulty": "1", + "hint": "hint1", + "tags": [1]} response = self.client.put(self.url, data=data) self.assertEqual(response.data, {"code": 1, "data": u"该题目不存在!"}) @@ -66,16 +85,29 @@ class ProblemAdminTest(APITestCase): ProblemTag.objects.create(name="tag1", description="destag1") ProblemTag.objects.create(name="tag2", description="destag2") tags = ProblemTag.objects.filter(id__in=[1]) - problem0 = Problem.objects.create(title="title1", description="des1", - test_case_id="1", source="source1", + problem0 = Problem.objects.create(title="title1", + description="des1", + test_case_id="1", + source="source1", sample=[{"input": "1 1", "output": "2"}], - time_limit=100, memory_limit=1000, - difficulty=1, hint="hint1", + time_limit=100, + memory_limit=1000, + difficulty=1, + hint="hint1", created_by=User.objects.get(username="test")) problem0.tags.add(*tags) - data = {"id": 1, "title": "title1", "description": "des1", "test_case_id": "1", "source": "source1", - "sample": [{"input": "1 1", "output": "2"}], "time_limit": "100", "memory_limit": "1000", - "difficulty": "1", "hint": "hint1", "visible": True, "tags": [1, 2]} + data = {"id": 1, + "title": "title1", + "description": "des1", + "test_case_id": "1", + "source": "source1", + "sample": [{"input": "1 1", "output": "2"}], + "time_limit": "100", + "memory_limit": "1000", + "difficulty": "1", + "hint": "hint1", + "visible": True, + "tags": [1, 2]} problem = Problem.objects.get(id=data["id"]) problem.tags.remove(*problem.tags.all()) problem.tags.add(*ProblemTag.objects.filter(id__in=data["tags"])) diff --git a/problem/views.py b/problem/views.py index cf45d93..12564b0 100644 --- a/problem/views.py +++ b/problem/views.py @@ -86,7 +86,7 @@ class ProblemAPIView(APIView): """ 题目分页json api接口 --- - response_serializer: ProblemSerializer + response_serializer: ProblemSerializer """ problem = Problem.objects.all().order_by("-last_update_time") visible = request.GET.get("visible", None) From 76cf78a67913f9742bbc8c2e7c8ca9d3654a6085 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Mon, 10 Aug 2015 15:01:40 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E5=A2=9E=E5=8A=A0announcement=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- announcement/views.py | 10 +++++++++- oj/urls.py | 1 + template/oj/announcement/announcement.html | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 template/oj/announcement/announcement.html diff --git a/announcement/views.py b/announcement/views.py index 654d91a..cdcd8a0 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -1,15 +1,23 @@ # coding=utf-8 from rest_framework.views import APIView +from django.shortcuts import render from utils.shortcuts import serializer_invalid_response, error_response, success_response -from account.models import User from utils.shortcuts import paginate from .models import Announcement from .serializers import (CreateAnnouncementSerializer, AnnouncementSerializer, EditAnnouncementSerializer) +def announcement_page(request, announcement_id): + try: + announcement = Announcement.objects.get(id=announcement_id, visible=True) + except Announcement.DoesNotExist: + return render(request, "utils/error.html", {"error": u"模板不存在"}) + return render(request, "oj/announcement/announcement.html", {"announcement": announcement}) + + class AnnouncementAdminAPIView(APIView): def post(self, request): """ diff --git a/oj/urls.py b/oj/urls.py index 9e4f08d..03fb4ee 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -26,6 +26,7 @@ urlpatterns = [ url(r'^api/admin/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_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", name="announcement_page"), url(r'^api/announcements/$', AnnouncementAPIView.as_view(), name="announcement_list_api"), url(r'^api/users/$', UserAPIView.as_view(), name="user_list_api"), diff --git a/template/oj/announcement/announcement.html b/template/oj/announcement/announcement.html new file mode 100644 index 0000000..18585d6 --- /dev/null +++ b/template/oj/announcement/announcement.html @@ -0,0 +1,22 @@ +{% extends "oj_base.html" %} + +{% block body %} +
+

{{ announcement.title }}

+ +

+ 作者:{{ announcement.created_by }} +     + 创建时间:{{ announcement.create_time }} + {% ifequal announcement.create_time announcement.last_update_time %} + {% else %} + 最后更新:{{ announcement.last_update_time }} + {% endifequal %} +

+ + +
+

{{ announcement.content|safe }}

+
+
+{% endblock %} \ No newline at end of file From 4b91d7b6453032320d1622770fd5730f6121441f Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Mon, 10 Aug 2015 15:02:40 +0800 Subject: [PATCH 4/6] =?UTF-8?q?announcement=E6=A8=A1=E6=9D=BF=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- template/oj/announcement/announcement.html | 1 + 1 file changed, 1 insertion(+) diff --git a/template/oj/announcement/announcement.html b/template/oj/announcement/announcement.html index 18585d6..f2e4d67 100644 --- a/template/oj/announcement/announcement.html +++ b/template/oj/announcement/announcement.html @@ -10,6 +10,7 @@ 创建时间:{{ announcement.create_time }} {% ifequal announcement.create_time announcement.last_update_time %} {% else %} +     最后更新:{{ announcement.last_update_time }} {% endifequal %}

From 1caa134d1b2250c60bc7ad47dc7aeccc3d343212 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 10 Aug 2015 15:18:37 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BC=BA=E5=B0=91=20test?= =?UTF-8?q?=20url=20=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/test_urls.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 admin/test_urls.py diff --git a/admin/test_urls.py b/admin/test_urls.py new file mode 100644 index 0000000..9f993aa --- /dev/null +++ b/admin/test_urls.py @@ -0,0 +1,10 @@ +# coding=utf-8 +from django.conf.urls import include, url +from django.views.generic import TemplateView + + +urlpatterns = [ + url(r'^admin/$', TemplateView.as_view(template_name="admin/admin.html"), name="admin_spa_page"), + url(r'^api/admin/test/$', "admin.tests.middleware_test_func"), + url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"), +] From ecd32618aed8dfd18762a2d1cab43c2f3a36138e Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Mon, 10 Aug 2015 15:23:52 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E5=88=A0=E9=99=A4=20print?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problem/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/problem/views.py b/problem/views.py index d7970c0..0a5490f 100644 --- a/problem/views.py +++ b/problem/views.py @@ -60,7 +60,6 @@ class ProblemAdminAPIView(APIView): serializer = EditProblemSerializer(data=request.data) if serializer.is_valid(): data = serializer.data - print request.data try: problem = Problem.objects.get(id=data["id"]) except Problem.DoesNotExist: