mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2024-09-21 08:23:20 +00:00
Merge branch 'dev' into sxw-dev
This commit is contained in:
commit
b91ff0688a
@ -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
|
||||
|
@ -47,6 +47,7 @@ INSTALLED_APPS = (
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
'account',
|
||||
'utils',
|
||||
|
||||
'rest_framework',
|
||||
'rest_framework_swagger',
|
||||
|
@ -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<template_dir>\w+)/(?P<template_name>\w+).html', AdminTemplateView.as_view(), name="admin_template")
|
||||
]
|
||||
|
31
static/src/js/app/admin/admin.js
Normal file
31
static/src/js/app/admin/admin.js
Normal file
@ -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"
|
||||
});
|
||||
});
|
@ -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",
|
||||
|
122
template/admin/admin.html
Normal file
122
template/admin/admin.html
Normal file
@ -0,0 +1,122 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="renderer" content="webkit">
|
||||
|
||||
<title>在线评测系统 - 后台管理</title>
|
||||
|
||||
<!-- custom css begin -->
|
||||
{% block css_block %}{% endblock %}
|
||||
<!-- custom css end -->
|
||||
|
||||
<!-- global css begin -->
|
||||
<link href="/static/css/admin.css" rel="stylesheet">
|
||||
<!-- global css end -->
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
<!-- nav begin -->
|
||||
<nav class="navbar navbar-masthead navbar-default navbar-static-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
|
||||
aria-expanded="false" aria-controls="navbar">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">qduoj admin</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="active"><a href="#">主页</a></li>
|
||||
<li><a href="#about">题目</a></li>
|
||||
<li><a href="#contact">提交</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
李扬
|
||||
<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#">我的提交</a></li>
|
||||
<li><a href="#">我的资料</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="#">退出</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<!-- nav end -->
|
||||
|
||||
<!--browser happy begin -->
|
||||
<!--[if lt IE 9]>
|
||||
<div class="alert alert-danger text-center" role="alert">
|
||||
当前网页 <strong>不支持</strong> 你正在使用的浏览器. 为了正常的访问, 请 <a href="http://browsehappy.com/">升级你的浏览器</a>.
|
||||
</div>
|
||||
<![endif]-->
|
||||
<!-- browser happy end -->
|
||||
|
||||
<div class="container" ms-controller="admin">
|
||||
<div class="row">
|
||||
<!-- admin left begin-->
|
||||
<div class="col-md-2">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-header">List header</li>
|
||||
<li class="list-group-item" id="li-index"><a href="#index">主页</a></li>
|
||||
<li class="list-group-item" id="li-announcement"><a href="#announcement">公告</a></li>
|
||||
<li class="list-group-item"><a href="#">Applications</a></li>
|
||||
<li class="list-group-header">Another list header</li>
|
||||
<li class="list-group-item"><a href="#">Help</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- admin left end -->
|
||||
|
||||
<!-- custom body begin -->
|
||||
<div ms-include-src="template_url"></div>
|
||||
<!-- custom body end -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal fade" id="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">提示</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p id="modal-text"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/js/config.js"></script>
|
||||
<script src="/static/js/require.js"></script>
|
||||
<script>
|
||||
require(["bootstrap", "admin"]);
|
||||
</script>
|
||||
|
||||
<!-- footer begin -->
|
||||
<div class="footer">
|
||||
<p class="text-muted text-center">Copyright © 2015 青岛大学信息工程学院 创新实验室</p>
|
||||
</div>
|
||||
<!-- footer end -->
|
||||
</body>
|
||||
</html>
|
@ -1,4 +0,0 @@
|
||||
{% extends "admin_base.html" %}
|
||||
{% block body %}
|
||||
Hello world
|
||||
{% endblock %}
|
1
template/admin/index/index.html
Normal file
1
template/admin/index/index.html
Normal file
@ -0,0 +1 @@
|
||||
<h1>Hello world</h1>
|
@ -65,14 +65,14 @@
|
||||
<![endif]-->
|
||||
<!-- browser happy end -->
|
||||
|
||||
<div class="container">
|
||||
<div class="container" ms-controller="admin">
|
||||
<div class="row">
|
||||
<!-- admin left begin-->
|
||||
<div class="col-md-2">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-header">List header</li>
|
||||
<li class="list-group-item active"><a href="#">Home</a></li>
|
||||
<li class="list-group-item"><a href="#">Library</a></li>
|
||||
<li class="list-group-item" id="li-index"><a href="#index">主页</a></li>
|
||||
<li class="list-group-item" id="li-announcement"><a href="#announcement">公告</a></li>
|
||||
<li class="list-group-item"><a href="#">Applications</a></li>
|
||||
<li class="list-group-header">Another list header</li>
|
||||
<li class="list-group-item"><a href="#">Help</a></li>
|
||||
@ -108,7 +108,7 @@
|
||||
<script src="/static/js/config.js"></script>
|
||||
<script src="/static/js/require.js"></script>
|
||||
<script>
|
||||
require(["bootstrap"]);
|
||||
require(["bootstrap", "admin"]);
|
||||
</script>
|
||||
{% block js_block %}{% endblock %}
|
||||
<!-- footer begin -->
|
||||
|
@ -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
|
||||
|
@ -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})
|
||||
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)
|
9
utils/test_urls.py
Normal file
9
utils/test_urls.py
Normal file
@ -0,0 +1,9 @@
|
||||
# coding=utf-8
|
||||
from django.conf.urls import include, url
|
||||
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^paginate_test/$', "utils.tests.pagination_test_func"),
|
||||
]
|
||||
|
64
utils/tests.py
Normal file
64
utils/tests.py
Normal file
@ -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)
|
Loading…
Reference in New Issue
Block a user