diff --git a/admin/views.py b/admin/views.py index 91ea44a2..78762c02 100644 --- a/admin/views.py +++ b/admin/views.py @@ -1,3 +1,14 @@ -from django.shortcuts import render +# coding=utf-8 +from django.conf import settings +from django.http import HttpResponse, Http404 -# Create your views here. +from rest_framework.views import APIView + + +class AdminTemplateView(APIView): + def get(self, request, template_dir, template_name): + path = settings.TEMPLATE_DIRS[0] + "/admin/" + template_dir + "/" + template_name + ".html" + try: + return HttpResponse(open(path).read(), content_type="text/html") + except IOError: + raise Http404 diff --git a/oj/settings.py b/oj/settings.py index db9ea8f9..793f6056 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -47,6 +47,7 @@ INSTALLED_APPS = ( 'django.contrib.staticfiles', 'account', + 'utils', 'rest_framework', 'rest_framework_swagger', diff --git a/oj/urls.py b/oj/urls.py index 0c8ea519..cc49f420 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -5,11 +5,12 @@ from django.views.generic import TemplateView from account.views import UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView from announcement.views import AnnouncementAPIView +from admin.views import AdminTemplateView urlpatterns = [ url("^$", TemplateView.as_view(template_name="oj/index.html"), name="index_page"), url(r'^docs/', include('rest_framework_swagger.urls')), - url(r'^admin/$', TemplateView.as_view(template_name="admin/index.html"), name="admin_index_page"), + url(r'^admin/$', TemplateView.as_view(template_name="admin/admin.html"), name="admin_spa_page"), url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"), url(r'^register/$', TemplateView.as_view(template_name="oj/account/register.html"), name="user_register_page"), url(r'^change_password/$', TemplateView.as_view(template_name="oj/account/change_password.html"), name="user_change_password_page"), @@ -22,4 +23,5 @@ urlpatterns = [ url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), name="add_contest_page"), 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") ] diff --git a/static/src/js/app/admin/admin.js b/static/src/js/app/admin/admin.js new file mode 100644 index 00000000..d3e30ac8 --- /dev/null +++ b/static/src/js/app/admin/admin.js @@ -0,0 +1,31 @@ +define("admin", ["jquery", "avalon"], function($, avalon){ + function li_active(selector){ + $(selector).attr("class", "list-group-item active"); + } + + function li_inactive(selector){ + $(".list-group-item").attr("class", "list-group-item"); + } + + var hash = window.location.hash.substring(1); + + if(hash){ + li_active("#li-" + hash); + }else { + li_active("#li-index"); + } + + window.onhashchange = function() { + var hash = window.location.hash.substring(1); + if(hash){ + li_inactive(".list-group-item"); + li_active("#li-" + hash); + vm.template_url = "template/index/" + hash + ".html"; + } + }; + + var vm = avalon.define({ + $id: "admin", + template_url: "template/index/index.html" + }); +}); \ No newline at end of file diff --git a/static/src/js/config.js b/static/src/js/config.js index 30f49699..dc8b37c2 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -15,6 +15,7 @@ var require = { submit_code: "app/oj/problem/submit_code", contest: "app/admin/contest/contest", csrf: "utils/csrf", + admin: "app/admin/admin", //formValidation 不要在代码中单独使用,而是使用和修改utils/validation base: "lib/formValidation/base", diff --git a/template/admin/admin.html b/template/admin/admin.html new file mode 100644 index 00000000..c7120847 --- /dev/null +++ b/template/admin/admin.html @@ -0,0 +1,122 @@ + + + + + + + + + + + 在线评测系统 - 后台管理 + + + {% block css_block %}{% endblock %} + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+ + + +
+ +
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/template/admin/index.html b/template/admin/index.html deleted file mode 100644 index df51a668..00000000 --- a/template/admin/index.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "admin_base.html" %} -{% block body %} -Hello world -{% endblock %} \ No newline at end of file diff --git a/template/admin/index/index.html b/template/admin/index/index.html new file mode 100644 index 00000000..61168280 --- /dev/null +++ b/template/admin/index/index.html @@ -0,0 +1 @@ +

Hello world

\ No newline at end of file diff --git a/template/admin_base.html b/template/admin_base.html index 8525b2ec..86cf7e28 100644 --- a/template/admin_base.html +++ b/template/admin_base.html @@ -65,14 +65,14 @@ -
+
  • List header
  • -
  • Home
  • -
  • Library
  • +
  • 主页
  • +
  • 公告
  • Applications
  • Another list header
  • Help
  • @@ -108,7 +108,7 @@ {% block js_block %}{% endblock %} diff --git a/tools/runtest.sh b/tools/runtest.sh index 87399cc1..f41a02b5 100644 --- a/tools/runtest.sh +++ b/tools/runtest.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash coverage run --source='.' manage.py test -coverage html -open htmlcov/index.html +test_result=$? +if [ "$test_result" -eq 0 ];then + coverage html + open htmlcov/index.html +fi diff --git a/utils/shortcuts.py b/utils/shortcuts.py index bd5f2868..852622f3 100644 --- a/utils/shortcuts.py +++ b/utils/shortcuts.py @@ -1,4 +1,6 @@ # coding=utf-8 +from django.core.paginator import Paginator + from rest_framework.response import Response @@ -11,4 +13,73 @@ def serializer_invalid_response(serializer): def success_response(data): - return Response(data={"code": 0, "data": data}) \ No newline at end of file + return Response(data={"code": 0, "data": data}) + + +def paginate(request, query_set, object_serializer): + """ + 用于分页的函数 + 如果 url 里面不含有paging=true,那么将返回全部数据。类似 + [ + { + "username": "1111111", + "password": "123456" + } + ] + 如果 url 中有 paging=true 的参数, + 然后还需要读取其余的两个参数,page=[int],需要的页码,p + age_size=[int],一页的数据条数 + 参数错误的时候,返回{"code": 1, "data": u"参数错误"} + 返回的数据格式 + { + "code": 0, + "data": { + "previous_page": null, + "results": [ + { + "username": "1111111", + "password": "123456" + } + ], + "next_page": 2 + } + } + :param query_set 数据库查询结果 + :param object_serializer: 序列化单个object的serializer + :return response + """ + need_paginate = request.GET.get("paging", None) + # 如果请求的参数里面没有paging=true的话 就返回全部数据 + if need_paginate != "true": + return success_response(data=object_serializer(query_set, many=True).data) + + page_size = request.GET.get("page_size", None) + if not page_size: + return error_response(u"参数错误") + + try: + page_size = int(page_size) + except Exception: + return error_response(u"参数错误") + + paginator = Paginator(query_set, page_size) + page = request.GET.get("page", None) + + try: + current_page = paginator.page(page) + except Exception: + return error_response(u"参数错误") + + data = {"results": object_serializer(current_page, many=True).data, "previous_page": None, "next_page": None} + + try: + data["previous_page"] = current_page.previous_page_number() + except Exception: + pass + + try: + data["next_page"] = current_page.next_page_number() + except Exception: + pass + + return success_response(data) \ No newline at end of file diff --git a/utils/test_urls.py b/utils/test_urls.py new file mode 100644 index 00000000..0500d16a --- /dev/null +++ b/utils/test_urls.py @@ -0,0 +1,9 @@ +# coding=utf-8 +from django.conf.urls import include, url + + + +urlpatterns = [ + url(r'^paginate_test/$', "utils.tests.pagination_test_func"), +] + diff --git a/utils/tests.py b/utils/tests.py new file mode 100644 index 00000000..519870ce --- /dev/null +++ b/utils/tests.py @@ -0,0 +1,64 @@ +# coding=utf-8 +from rest_framework.test import APIClient, APITestCase +from rest_framework import serializers +from rest_framework.decorators import api_view + +from account.models import User +from .shortcuts import paginate + + +class PaginationTestSerialiser(serializers.Serializer): + username = serializers.CharField(max_length=100) + + +@api_view(["GET"]) +def pagination_test_func(request): + return paginate(request, User.objects.all(), PaginationTestSerialiser) + + +class PaginatorTest(APITestCase): + urls = "utils.test_urls" + + def setUp(self): + self.client = APIClient() + self.url = "/paginate_test/" + User.objects.create(username="test1") + User.objects.create(username="test2") + + def test_no_paginate(self): + response = self.client.get(self.url) + self.assertEqual(response.data["code"], 0) + self.assertNotIn("next_page", response.data["data"]) + self.assertNotIn("previous_page", response.data["data"]) + + def test_error_parameter(self): + response = self.client.get(self.url + "?paging=true") + self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) + + response = self.client.get(self.url + "?paging=true&page_size=-1") + self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) + + response = self.client.get(self.url + "?paging=true&page_size=aa") + self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) + + response = self.client.get(self.url + "?paging=true&page_size=1&page=-1") + self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) + + response = self.client.get(self.url + "?paging=true&page_size=aaa&page=1") + self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) + + response = self.client.get(self.url + "?paging=true&page_size=1&page=aaa") + self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) + + def test_correct_paginate(self): + response = self.client.get(self.url + "?paging=true&limit=1&page_size=1&page=1") + self.assertEqual(response.data["code"], 0) + self.assertEqual(response.data["data"]["previous_page"], None) + self.assertEqual(response.data["data"]["next_page"], 2) + self.assertEqual(len(response.data["data"]["results"]), 1) + + response = self.client.get(self.url + "?paging=true&limit=1&page_size=2&page=1") + self.assertEqual(response.data["code"], 0) + self.assertEqual(response.data["data"]["previous_page"], None) + self.assertEqual(response.data["data"]["next_page"], None) + self.assertEqual(len(response.data["data"]["results"]), 2)