mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2024-12-29 16:41:56 +00:00
Merge branch 'virusdefender-dev' of https://git.coding.net/virusdefender/qduoj into hohoTT-dev
This commit is contained in:
commit
2fae6493d0
@ -47,6 +47,7 @@ INSTALLED_APPS = (
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
'account',
|
||||
'utils',
|
||||
|
||||
'rest_framework',
|
||||
'rest_framework_swagger',
|
||||
|
@ -11,6 +11,8 @@ urlpatterns = [
|
||||
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'^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"),
|
||||
url(r'^api/login/$', UserLoginAPIView.as_view(), name="user_login_api"),
|
||||
url(r'^api/register/$', UserRegisterAPIView.as_view(), name="user_register_api"),
|
||||
url(r'^api/change_password/$', UserChangePasswordAPIView.as_view(), name="user_change_password_api"),
|
||||
|
@ -5,4 +5,5 @@ django-redis-sessions
|
||||
djangorestframework
|
||||
django-rest-swagger
|
||||
celery
|
||||
gunicorn
|
||||
gunicorn
|
||||
coverage
|
69
static/src/js/app/oj/account/change_password.js
Normal file
69
static/src/js/app/oj/account/change_password.js
Normal file
@ -0,0 +1,69 @@
|
||||
require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrfHeader){
|
||||
$("#change_password-form").formValidation({
|
||||
framework: "bootstrap",
|
||||
fields: {
|
||||
username: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: "请填写用户名"
|
||||
}
|
||||
}
|
||||
},
|
||||
password: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: "请填写旧密码"
|
||||
}
|
||||
}
|
||||
},
|
||||
new_password: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: "请填写新密码"
|
||||
},
|
||||
stringLength: {
|
||||
min: 6,
|
||||
max: 30,
|
||||
message: '密码长度必须在6到30位之间'
|
||||
}
|
||||
},
|
||||
onSuccess: function(e, data) {
|
||||
data.fv.revalidateField('confirm_password');
|
||||
}
|
||||
},
|
||||
confirm_password: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: "请填写确认密码"
|
||||
},
|
||||
confirm: {
|
||||
original: $("#new_password"),
|
||||
message: "两次输入的密码必须一致"
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
).on('success.form.fv', function(e) {
|
||||
e.preventDefault();
|
||||
var username = $("#username").val();
|
||||
var new_password = $("#new_password ").val();
|
||||
var password = $("#password").val();
|
||||
$.ajax({
|
||||
beforeSend: csrfHeader,
|
||||
url: "/api/change_password/",
|
||||
data: {username: username, new_password: new_password , old_password : password},
|
||||
dataType: "json",
|
||||
method: "post",
|
||||
success: function (data) {
|
||||
|
||||
if(!data.code){
|
||||
window.location.href="/login/";
|
||||
}
|
||||
else{
|
||||
bs_alert(data.data);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
require(["jquery", "bs_alert", "validation"], function($, bs_alert){
|
||||
require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrfHeader){
|
||||
$("#login-form")
|
||||
.formValidation({
|
||||
framework: "bootstrap",
|
||||
@ -24,6 +24,7 @@ require(["jquery", "bs_alert", "validation"], function($, bs_alert){
|
||||
var username = $("#username").val();
|
||||
var password = $("#password").val();
|
||||
$.ajax({
|
||||
beforeSend: csrfHeader,
|
||||
url: "/api/login/",
|
||||
data: {username: username, password: password},
|
||||
dataType: "json",
|
||||
|
79
static/src/js/app/oj/account/register.js
Normal file
79
static/src/js/app/oj/account/register.js
Normal file
@ -0,0 +1,79 @@
|
||||
require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrfHeader){
|
||||
$("#register-form")
|
||||
.formValidation({
|
||||
framework: "bootstrap",
|
||||
fields: {
|
||||
username: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: "请填写用户名"
|
||||
},
|
||||
stringLength: {
|
||||
min: 3,
|
||||
max: 30,
|
||||
message: '用户名长度必须在3到30位之间'
|
||||
},
|
||||
usernameCheck:{
|
||||
message: '用户名已存在'
|
||||
}
|
||||
}
|
||||
},
|
||||
password: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: "请填写密码"
|
||||
},
|
||||
stringLength: {
|
||||
min: 6,
|
||||
max: 30,
|
||||
message: '密码长度必须在6到30位之间'
|
||||
}
|
||||
},
|
||||
onSuccess: function(e, data) {
|
||||
data.fv.revalidateField('confirm_password');
|
||||
}
|
||||
},
|
||||
real_name: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: "请填写真实姓名"
|
||||
}
|
||||
},
|
||||
},
|
||||
confirm_password: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: "请填写确认密码"
|
||||
},
|
||||
confirm: {
|
||||
original: $("#password"),
|
||||
message: "两次输入的密码必须一致"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
).on('success.form.fv', function(e) {
|
||||
e.preventDefault();
|
||||
var username = $("#username").val();
|
||||
var real_name = $("#real_name").val();
|
||||
var password = $("#password").val();
|
||||
$.ajax({
|
||||
beforeSend: csrfHeader,
|
||||
url: "/api/register/",
|
||||
data: {username: username, real_name: real_name, password: password},
|
||||
dataType: "json",
|
||||
method: "post",
|
||||
success: function (data) {
|
||||
if(!data.code){
|
||||
window.location.href="/login/";
|
||||
}
|
||||
else{
|
||||
bs_alert(data.data);
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
});
|
@ -14,6 +14,7 @@ var require = {
|
||||
bs_alert: "utils/bs_alert",
|
||||
submit_code: "app/oj/problem/submit_code",
|
||||
contest: "app/admin/contest/contest",
|
||||
csrf: "utils/csrf",
|
||||
|
||||
//formValidation 不要在代码中单独使用,而是使用和修改utils/validation
|
||||
base: "lib/formValidation/base",
|
||||
@ -25,7 +26,8 @@ var require = {
|
||||
"validator/date": "lib/formValidation/validator/date",
|
||||
"validator/integer": "lib/formValidation/validator/integer",
|
||||
"validator/between": "lib/formValidation/validator/between",
|
||||
|
||||
'validator/confirm':"lib/formValidation/validator/confirm",
|
||||
"validator/usernameCheck":"lib/formValidation/validator/usernameCheck",
|
||||
//富文本编辑器 不要直接使用,而是使用上面的editor
|
||||
simditor: "lib/simditor/simditor",
|
||||
"simple-module": "lib/simditor/module",
|
||||
|
32
static/src/js/lib/formValidation/validator/confirm.js
Normal file
32
static/src/js/lib/formValidation/validator/confirm.js
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* confirm validator
|
||||
*/
|
||||
|
||||
(function(root, factory) {
|
||||
|
||||
"use strict";
|
||||
|
||||
// AMD module is defined
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define("validator/confirm", ["jquery", "base"], factory);
|
||||
} else {
|
||||
// planted over the root!
|
||||
factory(root.jQuery, root.FormValidation);
|
||||
}
|
||||
|
||||
}(this, function ($, FormValidation) {
|
||||
FormValidation.I18n = $.extend(true, FormValidation.I18n || {}, {
|
||||
'en_US': {
|
||||
confirm: {
|
||||
'default': 'Please input the same value'
|
||||
}
|
||||
}
|
||||
});
|
||||
FormValidation.Validator.confirm = {
|
||||
validate: function(validator, $field, options) {
|
||||
if (options.original.val() == $field.val() || $field.val()== '')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}));
|
37
static/src/js/lib/formValidation/validator/usernameCheck.js
Normal file
37
static/src/js/lib/formValidation/validator/usernameCheck.js
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* usernameCheck validator
|
||||
*/
|
||||
(function(root, factory) {
|
||||
|
||||
"use strict";
|
||||
|
||||
// AMD module is defined
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define("validator/usernameCheck", ["jquery", "base", "csrf"], factory);
|
||||
} else {
|
||||
// planted over the root!
|
||||
factory(root.jQuery, root.FormValidation);
|
||||
}
|
||||
}(this, function ($, FormValidation, csrfHeader) {
|
||||
FormValidation.I18n = $.extend(true, FormValidation.I18n || {}, {
|
||||
'en_US': {
|
||||
usernameCheck: {
|
||||
'default': 'Please input the same value'
|
||||
}
|
||||
}
|
||||
});
|
||||
FormValidation.Validator.usernameCheck = {
|
||||
validate: function(validator, $field, options) {
|
||||
if ($field.val() == '')
|
||||
return true;
|
||||
return !$.ajax({
|
||||
async: false,
|
||||
beforeSend: csrfHeader,
|
||||
url: "/api/username_check/",
|
||||
data: {username: $field.val()},
|
||||
dataType: "json",
|
||||
method: "post",
|
||||
}).responseJSON.data;
|
||||
}
|
||||
};
|
||||
}));
|
16
static/src/js/utils/csrf.js
Normal file
16
static/src/js/utils/csrf.js
Normal file
@ -0,0 +1,16 @@
|
||||
define("csrf",function(){
|
||||
function get_cookie(cookie_name) {
|
||||
var name = cookie_name + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') c = c.substring(1);
|
||||
if (c.indexOf(name) != -1) return c.substring(name.length, c.length);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
function csrfHeader(xhr){
|
||||
xhr.setRequestHeader("X-CSRFToken", get_cookie("csrftoken"));
|
||||
}
|
||||
return csrfHeader;
|
||||
});
|
@ -7,5 +7,8 @@ define("validation",
|
||||
'validator/stringLength',
|
||||
'validator/date',
|
||||
'validator/integer',
|
||||
'validator/between'], function () {
|
||||
'validator/between',
|
||||
'validator/confirm',
|
||||
'validator/usernameCheck'],
|
||||
function () {
|
||||
});
|
@ -1,10 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
{% extends "oj_base.html" %}
|
||||
{% block body %}
|
||||
<div class="container">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<h2 class="text-center">修改密码</h2>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<form id="change_password-form">
|
||||
<div class="form-group">
|
||||
<label for="username">用户名</label>
|
||||
<input type="text" class="form-control input-lg" id="username" name="username" placeholder="用户名"
|
||||
autofocus>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">旧密码</label>
|
||||
<input type="password" class="form-control input-lg" id="password" name="password" placeholder="密码">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new_password">新密码</label>
|
||||
<input type="password" class="form-control input-lg" id="new_password" name="new_password" placeholder="新密码">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm_password">确认密码</label>
|
||||
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password" placeholder="确认密码">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">提交</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block js_block %}
|
||||
<script src="/static/js/app/oj/account/change_password.js"></script>
|
||||
{% endblock %}
|
@ -1,10 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
{% extends "oj_base.html" %}
|
||||
{% block body %}
|
||||
<div class="container">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<h2 class="text-center">用户注册</h2>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<form id="register-form">
|
||||
<div class="form-group">
|
||||
<label for="username">用户名</label>
|
||||
<input type="text" class="form-control input-lg" id="username" name="username" placeholder="用户名"
|
||||
autofocus>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="real_name">真实姓名</label>
|
||||
<input type="text" class="form-control input-lg" id="real_name" name="real_name" placeholder="真实姓名">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">密码</label>
|
||||
<input type="password" class="form-control input-lg" id="password" name="password" placeholder="密码">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm_password">确认密码</label>
|
||||
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password" placeholder="确认密码">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">提交</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block js_block %}
|
||||
<script src="/static/js/app/oj/account/register.js"></script>
|
||||
{% endblock %}
|
5
tools/runserver.cmd
Normal file
5
tools/runserver.cmd
Normal file
@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
python manage.py runserver
|
||||
cls
|
||||
cd..
|
||||
python manage.py runserver
|
2
tools/runserver.sh
Normal file
2
tools/runserver.sh
Normal file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
python manage.py runserver
|
12
tools/runtest.cmd
Normal file
12
tools/runtest.cmd
Normal file
@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
coverage run --source='.' manage.py test
|
||||
coverage html
|
||||
cd htmlcov
|
||||
index.html
|
||||
cls
|
||||
cd..
|
||||
coverage run --source='.' manage.py test
|
||||
coverage html
|
||||
cd htmlcov
|
||||
index.html
|
||||
|
7
tools/runtest.sh
Normal file
7
tools/runtest.sh
Normal file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
coverage run --source='.' manage.py test
|
||||
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