From 90eaadbb1c1efad809879d404d2df7ff50d45153 Mon Sep 17 00:00:00 2001 From: sxw Date: Mon, 3 Aug 2015 16:34:55 +0800 Subject: [PATCH 001/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=B3=A8=E5=86=8C=E9=A1=B5=E9=9D=A2=E5=92=8C=E5=AF=B9?= =?UTF-8?q?=E5=BA=94=E7=9A=84urls=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 1 + template/oj/account/register.html | 42 ++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/oj/urls.py b/oj/urls.py index 260a42a..84b1363 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -10,6 +10,7 @@ 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'^api/login/$', UserLoginAPIView.as_view(), name="login_api"), url(r'^api/login/$', UserLoginAPIView.as_view(), name="user_login_api"), url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), diff --git a/template/oj/account/register.html b/template/oj/account/register.html index 71ce692..a93014c 100644 --- a/template/oj/account/register.html +++ b/template/oj/account/register.html @@ -1,10 +1,34 @@ - - - - - - - +{% extends "oj_base.html" %} +{% block body %} +
+
+

用户注册

- - \ No newline at end of file +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+{% endblock %} +{% block js_block %} + +{% endblock %} \ No newline at end of file From 2454db54b0e725dcfd1b1e502c82da66b0727876 Mon Sep 17 00:00:00 2001 From: sxw Date: Mon, 3 Aug 2015 16:37:20 +0800 Subject: [PATCH 002/304] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E7=A9=BA?= =?UTF-8?q?=E6=A0=BC=E5=92=8Ctab=E6=B7=B7=E5=90=88=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oj/urls.py b/oj/urls.py index 84b1363..b308320 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -10,7 +10,7 @@ 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'^register/$', TemplateView.as_view(template_name="oj/account/register.html"), name="user_register_page"), url(r'^api/login/$', UserLoginAPIView.as_view(), name="login_api"), url(r'^api/login/$', UserLoginAPIView.as_view(), name="user_login_api"), url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), From c72624e2d5e997e18a741d2207694d5c8757b22f Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 4 Aug 2015 13:22:37 +0800 Subject: [PATCH 003/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20login=5Frequired?= =?UTF-8?q?=20decorator=20=E5=92=8C=E5=AF=B9=E5=BA=94=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/decorators.py | 24 +++++++++++++ account/test_urls.py | 12 +++++++ account/tests.py | 79 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 account/decorators.py create mode 100644 account/test_urls.py diff --git a/account/decorators.py b/account/decorators.py new file mode 100644 index 0000000..6b10896 --- /dev/null +++ b/account/decorators.py @@ -0,0 +1,24 @@ +# coding=utf-8 +from django.http import HttpResponse +from django.shortcuts import render + +from utils.shortcuts import error_response +from .models import User + + +def login_required(func): + def check(*args, **kwargs): + # 在class based views 里面,args 有两个元素,一个是self, 第二个才是request, + # 在function based views 里面,args 只有request 一个参数 + request = args[-1] + if request.user.is_authenticated(): + return func(*args, **kwargs) + if request.is_ajax(): + return error_response(u"请先登录") + else: + return render(request, "utils/error.html", {"error": u"请先登录"}) + return check + + +def admin_required(): + pass diff --git a/account/test_urls.py b/account/test_urls.py new file mode 100644 index 0000000..4066721 --- /dev/null +++ b/account/test_urls.py @@ -0,0 +1,12 @@ +# coding=utf-8 +from django.conf.urls import include, url + +from .tests import LoginRequiredCBVTestWithArgs, LoginRequiredCBVTestWithoutArgs + + +urlpatterns = [ + url(r'^test/fbv/1/$', "account.tests.login_required_FBV_test_without_args"), + url(r'^test/fbv/(?P\d+)/$', "account.tests.login_required_FBC_test_with_args"), + url(r'^test/cbv/1/$', LoginRequiredCBVTestWithoutArgs.as_view()), + url(r'^test/cbv/(?P\d+)/$', LoginRequiredCBVTestWithArgs.as_view()), +] diff --git a/account/tests.py b/account/tests.py index 90e5d12..83226b7 100644 --- a/account/tests.py +++ b/account/tests.py @@ -1,10 +1,16 @@ # coding=utf-8 +import json + from django.core.urlresolvers import reverse from django.test import TestCase, Client +from django.http import HttpResponse from rest_framework.test import APITestCase, APIClient +from rest_framework.views import APIView +from rest_framework.response import Response from .models import User +from .decorators import login_required class UserLoginTest(TestCase): @@ -98,4 +104,75 @@ class UserChangePasswordAPITest(APITestCase): def test_username_does_not_exist(self): data = {"username": "test1", "old_password": "aaabbb", "new_password": "aaaddd"} response = self.client.post(self.url, data=data) - self.assertEqual(response.data["code"], 1) \ No newline at end of file + self.assertEqual(response.data["code"], 1) + + +@login_required +def login_required_FBV_test_without_args(request): + return HttpResponse("function based view test1") + + +@login_required +def login_required_FBC_test_with_args(request, problem_id): + return HttpResponse(problem_id) + + +class LoginRequiredCBVTestWithoutArgs(APIView): + @login_required + def get(self, request): + return HttpResponse("class based view login required test1") + +class LoginRequiredCBVTestWithArgs(APIView): + @login_required + def get(self, request, problem_id): + return HttpResponse(problem_id) + + +class LoginRequiredDecoratorTest(TestCase): + urls = 'account.test_urls' + + def setUp(self): + self.client = Client() + user = User.objects.create(username="test") + user.set_password("test") + user.save() + + def test_fbv_without_args(self): + # 没登陆 + response = self.client.get("/test/fbv/1/") + self.assertTemplateUsed(response, "utils/error.html") + + # 登陆后 + self.client.login(username="test", password="test") + response = self.client.get("/test/fbv/1/") + self.assertEqual(response.content, "function based view test1") + + def test_fbv_with_args(self): + # 没登陆 + response = self.client.get("/test/fbv/1024/") + self.assertTemplateUsed(response, "utils/error.html") + + # 登陆后 + self.client.login(username="test", password="test") + response = self.client.get("/test/fbv/1024/") + self.assertEqual(response.content, "1024") + + def test_cbv_without_args(self): + # 没登陆 + response = self.client.get("/test/cbv/1/") + self.assertTemplateUsed(response, "utils/error.html") + + # 登陆后 + self.client.login(username="test", password="test") + response = self.client.get("/test/cbv/1/") + self.assertEqual(response.content, "class based view login required test1") + + def test_cbv_with_args(self): + # 没登陆 + response = self.client.get("/test/cbv/1024/", HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(json.loads(response.content), {"code": 1, "data": u"请先登录"}) + + # 登陆后 + self.client.login(username="test", password="test") + response = self.client.get("/test/cbv/1024/") + self.assertEqual(response.content, "1024") From 3ccb7740f2f671efecc711f35a9d11735d7d6f5d Mon Sep 17 00:00:00 2001 From: sxw Date: Tue, 4 Aug 2015 14:55:38 +0800 Subject: [PATCH 004/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E5=AF=86=E7=A0=81=E7=9A=84=E9=A1=B5=E9=9D=A2=E5=92=8C?= =?UTF-8?q?js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 1 + .../src/js/app/oj/account/change_password.js | 89 +++++++++++++++++++ static/src/js/config.js | 1 + .../lib/formValidation/validator/confirm.js | 34 +++++++ template/oj/account/change_password.html | 42 +++++++-- 5 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 static/src/js/app/oj/account/change_password.js create mode 100644 static/src/js/lib/formValidation/validator/confirm.js diff --git a/oj/urls.py b/oj/urls.py index b308320..f9d4cbf 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -11,6 +11,7 @@ urlpatterns = [ 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="login_api"), url(r'^api/login/$', UserLoginAPIView.as_view(), name="user_login_api"), url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), diff --git a/static/src/js/app/oj/account/change_password.js b/static/src/js/app/oj/account/change_password.js new file mode 100644 index 0000000..035e0d3 --- /dev/null +++ b/static/src/js/app/oj/account/change_password.js @@ -0,0 +1,89 @@ + +require(["jquery", "bs_alert", "validation"], function($, bs_alert){ + + + $("#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位之间' + }, + confirm: { + firstPassword: $("#new_password"), + secondPassword: $("#confirm_password"), + message: "两次输入的密码必须一致" + } + }, + onSuccess: function(e, data) { + + if (!data.fv.isValidField('confirm_password')) { + data.fv.revalidateField('confirm_password'); + } + } + + }, + confirm_password: { + validators: { + notEmpty: { + message: "请填写确认密码" + }, + confirm: { + firstPassword: $("#new_password"), + secondPassword: $("#confirm_password"), + message: "两次输入的密码必须一致" + } + }, + onSuccess: function(e, data) { + + if (!data.fv.isValidField('new_password')) { + data.fv.revalidateField('new_password'); + } + } + } + } + } + ).on('success.form.fv', function(e) { + e.preventDefault(); + var username = $("#username").val(); + var new_password = $("#new_password ").val(); + var password = $("#password").val(); + $.ajax({ + 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); + } + } + + }) + }); + +}); \ No newline at end of file diff --git a/static/src/js/config.js b/static/src/js/config.js index dc17a5a..fbeaa3a 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -25,6 +25,7 @@ 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", //富文本编辑器 不要直接使用,而是使用上面的editor simditor: "lib/simditor/simditor", diff --git a/static/src/js/lib/formValidation/validator/confirm.js b/static/src/js/lib/formValidation/validator/confirm.js new file mode 100644 index 0000000..5d491ee --- /dev/null +++ b/static/src/js/lib/formValidation/validator/confirm.js @@ -0,0 +1,34 @@ +/** + * 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.firstPassword.val() == options.secondPassword.val() ||options.secondPassword.val()== '') + return true; + return false; + } + }; +})); diff --git a/template/oj/account/change_password.html b/template/oj/account/change_password.html index 71ce692..65de447 100644 --- a/template/oj/account/change_password.html +++ b/template/oj/account/change_password.html @@ -1,10 +1,34 @@ - - - - - - - +{% extends "oj_base.html" %} +{% block body %} +
+
+

修改密码

- - \ No newline at end of file +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+{% endblock %} +{% block js_block %} + +{% endblock %} \ No newline at end of file From 58a664af1b0852b95517c11cf85d8a04bc1e6770 Mon Sep 17 00:00:00 2001 From: sxw Date: Tue, 4 Aug 2015 14:56:17 +0800 Subject: [PATCH 005/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E6=B3=A8?= =?UTF-8?q?=E5=86=8Cjs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/oj/account/register.js | 94 ++++++++++++++++++++++++ static/src/js/utils/validation.js | 5 +- 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 static/src/js/app/oj/account/register.js diff --git a/static/src/js/app/oj/account/register.js b/static/src/js/app/oj/account/register.js new file mode 100644 index 0000000..0241698 --- /dev/null +++ b/static/src/js/app/oj/account/register.js @@ -0,0 +1,94 @@ +require(["jquery", "bs_alert", "validation"], function($, bs_alert){ + + $("#register-form") + .formValidation({ + framework: "bootstrap", + fields: { + username: { + validators: { + notEmpty: { + message: "请填写用户名" + }, + stringLength: { + min: 3, + max: 30, + message: '用户名长度必须在3到30位之间' + } + } + }, + password: { + validators: { + notEmpty: { + message: "请填写密码" + }, + stringLength: { + min: 6, + max: 30, + message: '密码长度必须在6到30位之间' + }, + confirm: { + firstPassword: $("#password"), + secondPassword: $("#confirm_password"), + message: "两次输入的密码必须一致" + } + }, + onSuccess: function(e, data) { + + if (!data.fv.isValidField('confirm_password')) { + data.fv.revalidateField('confirm_password'); + } + } + }, + real_name: { + validators: { + notEmpty: { + message: "请填写真实姓名" + } + }, + + }, + confirm_password: { + validators: { + notEmpty: { + message: "请填写确认密码" + }, + confirm: { + firstPassword: $("#password"), + secondPassword: $("#confirm_password"), + message: "两次输入的密码必须一致" + } + + }, + + onSuccess: function(e, data) { + + if (!data.fv.isValidField('password')) { + data.fv.revalidateField('password'); + } + } + } + } + } + ).on('success.form.fv', function(e) { + e.preventDefault(); + var username = $("#username").val(); + var real_name = $("#real_name").val(); + var password = $("#password").val(); + $.ajax({ + 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); + } + } + + }) + }); + +}); \ No newline at end of file diff --git a/static/src/js/utils/validation.js b/static/src/js/utils/validation.js index ab3c2e7..b79bd81 100644 --- a/static/src/js/utils/validation.js +++ b/static/src/js/utils/validation.js @@ -7,5 +7,8 @@ define("validation", 'validator/stringLength', 'validator/date', 'validator/integer', - 'validator/between'], function () { + 'validator/between', + 'validator/confirm'], + function () { + }); \ No newline at end of file From 62a9e050f5ea05cc271edb31c37d505bc08f992e Mon Sep 17 00:00:00 2001 From: sxw Date: Tue, 4 Aug 2015 16:05:40 +0800 Subject: [PATCH 006/304] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E7=99=BB?= =?UTF-8?q?=E9=99=86=EF=BC=8C=E4=BF=AE=E6=94=B9=E5=AF=86=E7=A0=81=EF=BC=8C?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E4=B8=89=E4=B8=AA=E9=A1=B5=E9=9D=A2=E7=9A=84?= =?UTF-8?q?csrf=EF=BC=9B=20=E6=B7=BB=E5=8A=A0=E4=BA=86usernameCheck?= =?UTF-8?q?=E7=9A=84valuedation=E6=A3=80=E6=B5=8B=E6=96=B9=E6=B3=95;=20url?= =?UTF-8?q?s.py=20=E6=B7=BB=E5=8A=A0=E4=BA=86register=EF=BC=8Cchange=5Fpas?= =?UTF-8?q?sword=E9=A1=B5=E9=9D=A2=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 2 + .../src/js/app/oj/account/change_password.js | 3 +- static/src/js/app/oj/account/login.js | 3 +- static/src/js/app/oj/account/register.js | 6 ++- static/src/js/config.js | 3 +- .../formValidation/validator/usernameCheck.js | 44 +++++++++++++++++++ static/src/js/utils/csrf.js | 16 +++++++ static/src/js/utils/validation.js | 3 +- 8 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 static/src/js/lib/formValidation/validator/usernameCheck.js create mode 100644 static/src/js/utils/csrf.js diff --git a/oj/urls.py b/oj/urls.py index fc652b3..aa35a62 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -10,6 +10,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"), diff --git a/static/src/js/app/oj/account/change_password.js b/static/src/js/app/oj/account/change_password.js index 035e0d3..ff9bf74 100644 --- a/static/src/js/app/oj/account/change_password.js +++ b/static/src/js/app/oj/account/change_password.js @@ -1,5 +1,5 @@ -require(["jquery", "bs_alert", "validation"], function($, bs_alert){ +require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrfHeader){ $("#change_password-form").formValidation({ @@ -69,6 +69,7 @@ require(["jquery", "bs_alert", "validation"], function($, bs_alert){ 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", diff --git a/static/src/js/app/oj/account/login.js b/static/src/js/app/oj/account/login.js index b7c23e7..da4416d 100644 --- a/static/src/js/app/oj/account/login.js +++ b/static/src/js/app/oj/account/login.js @@ -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", diff --git a/static/src/js/app/oj/account/register.js b/static/src/js/app/oj/account/register.js index 0241698..4baf334 100644 --- a/static/src/js/app/oj/account/register.js +++ b/static/src/js/app/oj/account/register.js @@ -1,4 +1,4 @@ -require(["jquery", "bs_alert", "validation"], function($, bs_alert){ +require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrfHeader){ $("#register-form") .formValidation({ @@ -13,6 +13,9 @@ require(["jquery", "bs_alert", "validation"], function($, bs_alert){ min: 3, max: 30, message: '用户名长度必须在3到30位之间' + }, + usernameCheck:{ + message: '用户名已存在' } } }, @@ -75,6 +78,7 @@ require(["jquery", "bs_alert", "validation"], function($, bs_alert){ 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", diff --git a/static/src/js/config.js b/static/src/js/config.js index fbeaa3a..3907d1c 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -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", @@ -26,7 +27,7 @@ var require = { "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", diff --git a/static/src/js/lib/formValidation/validator/usernameCheck.js b/static/src/js/lib/formValidation/validator/usernameCheck.js new file mode 100644 index 0000000..116c1dc --- /dev/null +++ b/static/src/js/lib/formValidation/validator/usernameCheck.js @@ -0,0 +1,44 @@ +/** + * 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; + + } + }; +})); diff --git a/static/src/js/utils/csrf.js b/static/src/js/utils/csrf.js new file mode 100644 index 0000000..767542b --- /dev/null +++ b/static/src/js/utils/csrf.js @@ -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; +}); diff --git a/static/src/js/utils/validation.js b/static/src/js/utils/validation.js index b79bd81..38788f7 100644 --- a/static/src/js/utils/validation.js +++ b/static/src/js/utils/validation.js @@ -8,7 +8,8 @@ define("validation", 'validator/date', 'validator/integer', 'validator/between', - 'validator/confirm'], + 'validator/confirm', + 'validator/usernameCheck'], function () { }); \ No newline at end of file From 006ed117f1cc51704603af47597a40bcc0e34e20 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Tue, 4 Aug 2015 17:38:04 +0800 Subject: [PATCH 007/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=85=AC?= =?UTF-8?q?=E5=91=8A=E7=9A=84API=E6=8E=A5=E5=8F=A3=E5=92=8C=E7=9B=B8?= =?UTF-8?q?=E5=BA=94=E7=9A=84API=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- announcement/__init__.py | 0 announcement/admin.py | 3 +++ announcement/migrations/__init__.py | 0 announcement/models.py | 20 ++++++++++++++++++++ announcement/serializers.py | 8 ++++++++ announcement/tests.py | 16 ++++++++++++++++ announcement/views.py | 28 ++++++++++++++++++++++++++++ oj/urls.py | 2 ++ 8 files changed, 77 insertions(+) create mode 100644 announcement/__init__.py create mode 100644 announcement/admin.py create mode 100644 announcement/migrations/__init__.py create mode 100644 announcement/models.py create mode 100644 announcement/serializers.py create mode 100644 announcement/tests.py create mode 100644 announcement/views.py diff --git a/announcement/__init__.py b/announcement/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/announcement/admin.py b/announcement/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/announcement/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/announcement/migrations/__init__.py b/announcement/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/announcement/models.py b/announcement/models.py new file mode 100644 index 0000000..a47eba6 --- /dev/null +++ b/announcement/models.py @@ -0,0 +1,20 @@ +# coding=utf-8 +from django.db import models + +from account.models import User + + +class Announcement(models.Model): + # 标题 + title = models.CharField(max_length=50) + # 公告的描述 HTML 格式 + description = models.TextField() + # 创建时间 + create_time = models.DateTimeField(auto_now_add=True) + # 这个公告是谁创建的 + created_by = models.ForeignKey(User) + # 是否可见 false的话相当于删除 + visible = models.BooleanField(default=True) + + class Meta: + db_table = "announcement" diff --git a/announcement/serializers.py b/announcement/serializers.py new file mode 100644 index 0000000..a1525b9 --- /dev/null +++ b/announcement/serializers.py @@ -0,0 +1,8 @@ +# coding=utf-8 +from rest_framework import serializers + + +class AnnouncementSerializer(serializers.Serializer): + title = serializers.CharField(max_length=50) + description = serializers.CharField(max_length=10000) + diff --git a/announcement/tests.py b/announcement/tests.py new file mode 100644 index 0000000..ccb8a04 --- /dev/null +++ b/announcement/tests.py @@ -0,0 +1,16 @@ +# coding=utf-8 +from django.core.urlresolvers import reverse + +from rest_framework.test import APITestCase, APIClient + + +class AbstractAnnouncementAPITest(APITestCase): + def setUp(self): + self.client = APIClient() + self.url = reverse("announcement_api") + + def test_invalid_format(self): + # todo 判断用户是否登录 + data = {"title": "test1"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 1) diff --git a/announcement/views.py b/announcement/views.py new file mode 100644 index 0000000..220e806 --- /dev/null +++ b/announcement/views.py @@ -0,0 +1,28 @@ +# coding=utf-8 +from rest_framework.views import APIView + +from utils.shortcuts import serializer_invalid_response, error_response, success_response + +from account.models import User + +from .models import Announcement +from .serializers import AnnouncementSerializer + + +class AnnouncementAPIView(APIView): + # todo 判断用户是否需要登录 + def post(self, request): + """ + 公告发布json api接口 + --- + request_serializer: AnnouncementSerializer + """ + serializer = AnnouncementSerializer(data=request.DATA) + if serializer.is_valid(): + data = serializer.data + Announcement.objects.create(title=data["title"], + description=data["description"], + created_by=request.user) + return success_response(u"公告发布成功!") + else: + return serializer_invalid_response(serializer) \ No newline at end of file diff --git a/oj/urls.py b/oj/urls.py index fc652b3..19da589 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -4,6 +4,7 @@ from django.contrib import admin from django.views.generic import TemplateView from account.views import UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView +from announcement.views import AnnouncementAPIView urlpatterns = [ url("^$", TemplateView.as_view(template_name="oj/index.html"), name="index_page"), @@ -14,6 +15,7 @@ urlpatterns = [ url(r'^api/register/$', UserRegisterAPIView.as_view(), name="user_register_api"), url(r'^api/change_password/$', UserChangePasswordAPIView.as_view(), name="user_change_password_api"), url(r'^api/username_check/$', UsernameCheckAPIView.as_view(), name="username_check_api"), + url(r'^api/admin/announcement/$', AnnouncementAPIView.as_view(), name="announcement_api"), url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), name="add_contest_page"), From 76aba7f0e7f7b012425f6efcc5bae06daca1f40a Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 4 Aug 2015 18:38:00 +0800 Subject: [PATCH 008/304] add coverage requirement and run test shell script --- requirements.txt | 3 ++- tools/runserver.sh | 2 ++ tools/runtest.sh | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 tools/runserver.sh create mode 100644 tools/runtest.sh diff --git a/requirements.txt b/requirements.txt index 00da332..bb0fce3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,5 @@ django-redis-sessions djangorestframework django-rest-swagger celery -gunicorn \ No newline at end of file +gunicorn +coverage \ No newline at end of file diff --git a/tools/runserver.sh b/tools/runserver.sh new file mode 100644 index 0000000..d508cfc --- /dev/null +++ b/tools/runserver.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +python manage.py runserver \ No newline at end of file diff --git a/tools/runtest.sh b/tools/runtest.sh new file mode 100644 index 0000000..2fc4d4a --- /dev/null +++ b/tools/runtest.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +coverage run --source='.' manage.py test +nose html +open htmlcov/index.html From 1d4af5cc7cbffbba38ea9bd6ed951e6e10317f3d Mon Sep 17 00:00:00 2001 From: sxw Date: Tue, 4 Aug 2015 19:18:56 +0800 Subject: [PATCH 009/304] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86windows?= =?UTF-8?q?=E4=B8=8B=E7=9A=84runserver=E5=92=8Cruntest=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=20[ci=20sikp]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/runserver.cmd | 5 +++++ tools/runtest.cmd | 12 ++++++++++++ tools/runtest.sh | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tools/runserver.cmd create mode 100644 tools/runtest.cmd diff --git a/tools/runserver.cmd b/tools/runserver.cmd new file mode 100644 index 0000000..b458e55 --- /dev/null +++ b/tools/runserver.cmd @@ -0,0 +1,5 @@ +@echo off +python manage.py runserver +cls +cd.. +python manage.py runserver \ No newline at end of file diff --git a/tools/runtest.cmd b/tools/runtest.cmd new file mode 100644 index 0000000..e6d9ad7 --- /dev/null +++ b/tools/runtest.cmd @@ -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 + diff --git a/tools/runtest.sh b/tools/runtest.sh index 2fc4d4a..87399cc 100644 --- a/tools/runtest.sh +++ b/tools/runtest.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash coverage run --source='.' manage.py test -nose html +coverage html open htmlcov/index.html From 4548ffd61709941267f36a7463ad1fbf5d56b810 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Tue, 4 Aug 2015 19:23:10 +0800 Subject: [PATCH 010/304] =?UTF-8?q?=E6=9B=B4=E6=AD=A3=E5=85=AC=E5=91=8AAPI?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=9A=84=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- announcement/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/announcement/tests.py b/announcement/tests.py index ccb8a04..f73805e 100644 --- a/announcement/tests.py +++ b/announcement/tests.py @@ -4,7 +4,7 @@ from django.core.urlresolvers import reverse from rest_framework.test import APITestCase, APIClient -class AbstractAnnouncementAPITest(APITestCase): +class AnnouncementAPITest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse("announcement_api") From 0b55f6bbf79bf8f66c1ea81931165c5e3464feef Mon Sep 17 00:00:00 2001 From: sxw Date: Tue, 4 Aug 2015 19:54:44 +0800 Subject: [PATCH 011/304] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E5=AF=86=E7=A0=81=E9=AA=8C=E8=AF=81=E7=9A=84js?= =?UTF-8?q?=EF=BC=8C=E7=B2=BE=E7=AE=80=E4=BB=A3=E7=A0=81=E9=87=8F=EF=BC=8C?= =?UTF-8?q?=E5=87=8F=E5=B0=91=E4=BA=86=E9=87=8D=E5=A4=8D=E7=9A=84=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=EF=BC=8C=E5=87=8F=E5=B0=91=E4=BA=86valuedator?= =?UTF-8?q?=E4=B8=ADconfirm=E7=9A=84=E5=8F=82=E6=95=B0=20[ci=20skip]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/js/app/oj/account/change_password.js | 23 +------------------ static/src/js/app/oj/account/register.js | 23 ++----------------- .../lib/formValidation/validator/confirm.js | 10 ++++---- .../formValidation/validator/usernameCheck.js | 21 ++++++----------- static/src/js/utils/validation.js | 1 - 5 files changed, 14 insertions(+), 64 deletions(-) diff --git a/static/src/js/app/oj/account/change_password.js b/static/src/js/app/oj/account/change_password.js index ff9bf74..e3e907f 100644 --- a/static/src/js/app/oj/account/change_password.js +++ b/static/src/js/app/oj/account/change_password.js @@ -1,7 +1,4 @@ - require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrfHeader){ - - $("#change_password-form").formValidation({ framework: "bootstrap", fields: { @@ -28,20 +25,11 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf min: 6, max: 30, message: '密码长度必须在6到30位之间' - }, - confirm: { - firstPassword: $("#new_password"), - secondPassword: $("#confirm_password"), - message: "两次输入的密码必须一致" } }, onSuccess: function(e, data) { - - if (!data.fv.isValidField('confirm_password')) { data.fv.revalidateField('confirm_password'); - } } - }, confirm_password: { validators: { @@ -49,17 +37,10 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf message: "请填写确认密码" }, confirm: { - firstPassword: $("#new_password"), - secondPassword: $("#confirm_password"), + original: $("#new_password"), message: "两次输入的密码必须一致" } }, - onSuccess: function(e, data) { - - if (!data.fv.isValidField('new_password')) { - data.fv.revalidateField('new_password'); - } - } } } } @@ -83,8 +64,6 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf bs_alert(data.data); } } - }) }); - }); \ No newline at end of file diff --git a/static/src/js/app/oj/account/register.js b/static/src/js/app/oj/account/register.js index 4baf334..e6b4deb 100644 --- a/static/src/js/app/oj/account/register.js +++ b/static/src/js/app/oj/account/register.js @@ -1,5 +1,4 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrfHeader){ - $("#register-form") .formValidation({ framework: "bootstrap", @@ -28,18 +27,10 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf min: 6, max: 30, message: '密码长度必须在6到30位之间' - }, - confirm: { - firstPassword: $("#password"), - secondPassword: $("#confirm_password"), - message: "两次输入的密码必须一致" - } + } }, onSuccess: function(e, data) { - - if (!data.fv.isValidField('confirm_password')) { data.fv.revalidateField('confirm_password'); - } } }, real_name: { @@ -48,7 +39,6 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf message: "请填写真实姓名" } }, - }, confirm_password: { validators: { @@ -56,18 +46,9 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf message: "请填写确认密码" }, confirm: { - firstPassword: $("#password"), - secondPassword: $("#confirm_password"), + original: $("#password"), message: "两次输入的密码必须一致" } - - }, - - onSuccess: function(e, data) { - - if (!data.fv.isValidField('password')) { - data.fv.revalidateField('password'); - } } } } diff --git a/static/src/js/lib/formValidation/validator/confirm.js b/static/src/js/lib/formValidation/validator/confirm.js index 5d491ee..db5ad3a 100644 --- a/static/src/js/lib/formValidation/validator/confirm.js +++ b/static/src/js/lib/formValidation/validator/confirm.js @@ -22,13 +22,11 @@ } } }); - FormValidation.Validator.confirm = { - validate: function(validator, $field, options) { - if (options.firstPassword.val() == options.secondPassword.val() ||options.secondPassword.val()== '') - return true; - return false; - } + if (options.original.val() == $field.val() || $field.val()== '') + return true; + return false; + } }; })); diff --git a/static/src/js/lib/formValidation/validator/usernameCheck.js b/static/src/js/lib/formValidation/validator/usernameCheck.js index 116c1dc..36a7762 100644 --- a/static/src/js/lib/formValidation/validator/usernameCheck.js +++ b/static/src/js/lib/formValidation/validator/usernameCheck.js @@ -1,7 +1,6 @@ /** * usernameCheck validator */ - (function(root, factory) { "use strict"; @@ -13,7 +12,6 @@ // planted over the root! factory(root.jQuery, root.FormValidation); } - }(this, function ($, FormValidation, csrfHeader) { FormValidation.I18n = $.extend(true, FormValidation.I18n || {}, { 'en_US': { @@ -22,23 +20,18 @@ } } }); - 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; - + async: false, + beforeSend: csrfHeader, + url: "/api/username_check/", + data: {username: $field.val()}, + dataType: "json", + method: "post", + }).responseJSON.data; } }; })); diff --git a/static/src/js/utils/validation.js b/static/src/js/utils/validation.js index 38788f7..5fe792f 100644 --- a/static/src/js/utils/validation.js +++ b/static/src/js/utils/validation.js @@ -11,5 +11,4 @@ define("validation", 'validator/confirm', 'validator/usernameCheck'], function () { - }); \ No newline at end of file From 44be61dab6b21553367ea84f60f40e31cb587bf4 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 5 Aug 2015 08:43:15 +0800 Subject: [PATCH 012/304] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20runtest.sh?= =?UTF-8?q?=EF=BC=8C=E5=8F=AA=E6=9C=89=E5=9C=A8=E6=B5=8B=E8=AF=95=E6=88=90?= =?UTF-8?q?=E5=8A=9F=E7=9A=84=E6=83=85=E5=86=B5=E4=B8=8B=E6=89=8D=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E6=B5=8B=E8=AF=95=E8=A6=86=E7=9B=96=E7=8E=87=E5=88=86?= =?UTF-8?q?=E6=9E=90=E5=92=8C=E6=89=93=E5=BC=80=E7=BB=93=E6=9E=9C=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/runtest.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/runtest.sh b/tools/runtest.sh index 87399cc..f41a02b 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 From 8a6093d6457c1e5ddbeacf87575a95fa4ce1d222 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 5 Aug 2015 08:44:28 +0800 Subject: [PATCH 013/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=80=9A=E7=94=A8?= =?UTF-8?q?=E5=88=86=E9=A1=B5=E5=87=BD=E6=95=B0=E5=92=8C=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E7=9A=84=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/settings.py | 1 + utils/shortcuts.py | 49 ++++++++++++++++++++++++++++++++++- utils/test_urls.py | 9 +++++++ utils/tests.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 utils/test_urls.py create mode 100644 utils/tests.py diff --git a/oj/settings.py b/oj/settings.py index db9ea8f..793f605 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/utils/shortcuts.py b/utils/shortcuts.py index bd5f286..3d37063 100644 --- a/utils/shortcuts.py +++ b/utils/shortcuts.py @@ -1,4 +1,7 @@ # coding=utf-8 +from django.core.paginator import Paginator + +from rest_framework import pagination from rest_framework.response import Response @@ -11,4 +14,48 @@ 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): + """ + 用于分页的函数 + :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 0000000..0500d16 --- /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 0000000..9ab31f9 --- /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&limit=-1") + self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) + + response = self.client.get(self.url + "?paging=true&limit=aa") + self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) + + response = self.client.get(self.url + "?paging=true&limit=1&page_size=1&page=-1") + self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) + + response = self.client.get(self.url + "?paging=true&limit=1&page_size=aaa&page=1") + self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) + + response = self.client.get(self.url + "?paging=true&limit=1&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) From 5558600792c66135e97d349e1ee2d88a1324fb4b Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 5 Aug 2015 08:53:39 +0800 Subject: [PATCH 014/304] =?UTF-8?q?=E8=A1=A5=E5=85=85=E9=80=9A=E7=94=A8?= =?UTF-8?q?=E5=88=86=E9=A1=B5=E5=87=BD=E6=95=B0=E7=9A=84=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E5=92=8C=E7=94=A8=E6=B3=95=EF=BC=9B=E4=BF=AE=E6=94=B9=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=9A=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/shortcuts.py | 26 +++++++++++++++++++++++++- utils/tests.py | 10 +++++----- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/utils/shortcuts.py b/utils/shortcuts.py index 3d37063..852622f 100644 --- a/utils/shortcuts.py +++ b/utils/shortcuts.py @@ -1,7 +1,6 @@ # coding=utf-8 from django.core.paginator import Paginator -from rest_framework import pagination from rest_framework.response import Response @@ -20,6 +19,31 @@ def success_response(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 diff --git a/utils/tests.py b/utils/tests.py index 9ab31f9..519870c 100644 --- a/utils/tests.py +++ b/utils/tests.py @@ -35,19 +35,19 @@ class PaginatorTest(APITestCase): response = self.client.get(self.url + "?paging=true") self.assertEqual(response.data, {"code": 1, "data": u"参数错误"}) - response = self.client.get(self.url + "?paging=true&limit=-1") + 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&limit=aa") + 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&limit=1&page_size=1&page=-1") + 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&limit=1&page_size=aaa&page=1") + 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&limit=1&page_size=1&page=aaa") + 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): From 17b44800ca339d3b5302c1343508f704f17d6614 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Wed, 5 Aug 2015 10:34:00 +0800 Subject: [PATCH 015/304] =?UTF-8?q?=E5=B0=86announcement=20=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=20setings.py=20=E9=87=8C=E9=9D=A2=E7=9A=84=20INSTALLE?= =?UTF-8?q?D=5FAPPS=EF=BC=8C=E5=B9=B6=E4=B8=94=E5=88=9B=E5=BB=BAannounceme?= =?UTF-8?q?nt=E8=BF=99=E5=BC=A0=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- announcement/migrations/0001_initial.py | 29 +++++++++++++++++++++++++ oj/settings.py | 1 + 2 files changed, 30 insertions(+) create mode 100644 announcement/migrations/0001_initial.py diff --git a/announcement/migrations/0001_initial.py b/announcement/migrations/0001_initial.py new file mode 100644 index 0000000..24a1d13 --- /dev/null +++ b/announcement/migrations/0001_initial.py @@ -0,0 +1,29 @@ +# -*- 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), + ] + + operations = [ + migrations.CreateModel( + name='Announcement', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('title', models.CharField(max_length=50)), + ('description', models.TextField()), + ('create_time', models.DateTimeField(auto_now_add=True)), + ('visible', models.BooleanField(default=True)), + ('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'announcement', + }, + ), + ] diff --git a/oj/settings.py b/oj/settings.py index 793f605..910914c 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -47,6 +47,7 @@ INSTALLED_APPS = ( 'django.contrib.staticfiles', 'account', + 'announcement', 'utils', 'rest_framework', From 34ccdd5e0e285dd8bb92ba6bb4fedbf372d58668 Mon Sep 17 00:00:00 2001 From: sxw Date: Wed, 5 Aug 2015 19:38:28 +0800 Subject: [PATCH 016/304] =?UTF-8?q?1.=E4=BB=BF=E7=85=A7=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E9=87=8D=E5=86=99=E4=BA=86=E5=8E=9FusernameCheck=E7=9A=84valid?= =?UTF-8?q?ator=E3=80=82=E9=87=8D=E5=91=BD=E5=90=8D=E4=B8=BAremoteCSRF?= =?UTF-8?q?=EF=BC=8C=E7=94=A8=E4=BA=8E=E4=BD=BF=E7=94=A8ajax=E5=BC=82?= =?UTF-8?q?=E6=AD=A5=E5=90=91=E6=9C=8D=E5=8A=A1=E5=99=A8=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E5=94=AF=E4=B8=80=E6=80=A7=E3=80=82=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E4=B8=BAurl=EF=BC=9A=E8=AF=B7=E6=B1=82=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=EF=BC=8Cfield=EF=BC=9A=E5=90=91=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E5=8F=91=E9=80=81=E7=9A=84=E8=A1=A8=E7=A4=BA=E7=AC=A6?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=EF=BC=9B=202.=E6=B7=BB=E5=8A=A0=E4=BA=86emai?= =?UTF-8?q?lAddress=E7=9A=84validator=E7=9A=84requires.js=E7=9A=84config.?= =?UTF-8?q?=20[ci=20skip]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/config.js | 5 +- .../formValidation/validator/remoteCSRF.js | 46 +++++++++++++++++++ .../formValidation/validator/usernameCheck.js | 37 --------------- static/src/js/utils/validation.js | 3 +- 4 files changed, 51 insertions(+), 40 deletions(-) create mode 100644 static/src/js/lib/formValidation/validator/remoteCSRF.js delete mode 100644 static/src/js/lib/formValidation/validator/usernameCheck.js diff --git a/static/src/js/config.js b/static/src/js/config.js index 3907d1c..30f4969 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -26,8 +26,9 @@ 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", + "validator/confirm":"lib/formValidation/validator/confirm", + "validator/remoteCSRF":"lib/formValidation/validator/remoteCSRF", + "validator/emailAddress":"lib/formValidation/validator/emailAddress", //富文本编辑器 不要直接使用,而是使用上面的editor simditor: "lib/simditor/simditor", "simple-module": "lib/simditor/module", diff --git a/static/src/js/lib/formValidation/validator/remoteCSRF.js b/static/src/js/lib/formValidation/validator/remoteCSRF.js new file mode 100644 index 0000000..9cab354 --- /dev/null +++ b/static/src/js/lib/formValidation/validator/remoteCSRF.js @@ -0,0 +1,46 @@ +/** + * remoteCSRF validator + */ +(function(root, factory) { + + "use strict"; + + // AMD module is defined + if (typeof define === "function" && define.amd) { + define("validator/remoteCSRF", ["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': { + remoteCSRF: { + 'default': '' + } + } + }); + FormValidation.Validator.remoteCSRF = { + validate: function(validator, $field, options) { + var dfd = new $.Deferred(), ajaxData = {}; + ajaxData[options.field] = $field.val(); + if ($field.val() === '') + return true; + var url = options.url; + var xhr = $.ajax({ + beforeSend: csrfHeader, + url: url, + dataType: 'json', + data: ajaxData, + method: "post" + }); + xhr.success(function(response) { + dfd.resolve($field, 'remoteCSRF',{valid:!response.data, message:options.msg}); + }) + .error(function(response) { + dfd.resolve($field, 'remoteCSRF', {valid: false}); + }); + return dfd; + } + }; +})); diff --git a/static/src/js/lib/formValidation/validator/usernameCheck.js b/static/src/js/lib/formValidation/validator/usernameCheck.js deleted file mode 100644 index 36a7762..0000000 --- a/static/src/js/lib/formValidation/validator/usernameCheck.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * 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; - } - }; -})); diff --git a/static/src/js/utils/validation.js b/static/src/js/utils/validation.js index 5fe792f..45da8f5 100644 --- a/static/src/js/utils/validation.js +++ b/static/src/js/utils/validation.js @@ -9,6 +9,7 @@ define("validation", 'validator/integer', 'validator/between', 'validator/confirm', - 'validator/usernameCheck'], + 'validator/remoteCSRF', + 'validator/emailAddress'], function () { }); \ No newline at end of file From c868441d615e78402bd6b258941305ac52b78a23 Mon Sep 17 00:00:00 2001 From: sxw Date: Wed, 5 Aug 2015 19:40:02 +0800 Subject: [PATCH 017/304] =?UTF-8?q?1.=E5=9C=A8=E7=94=A8=E6=88=B7=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E9=A1=B5=E9=9D=A2=E6=B7=BB=E5=8A=A0=E4=BA=86email?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=EF=BC=8C=E5=B9=B6=E9=99=84=E5=B8=A6=E9=AA=8C?= =?UTF-8?q?=E8=AF=81;=202.=E7=BB=9F=E4=B8=80=E4=BA=86username=E5=92=8Cemai?= =?UTF-8?q?l=E5=AD=97=E6=AE=B5=E7=9A=84=E5=94=AF=E4=B8=80=E6=80=A7?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E6=96=B9=E6=B3=95=E3=80=82=20[ci=20skip]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/oj/account/register.js | 22 ++++++++++++++++++++-- template/oj/account/register.html | 4 ++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/static/src/js/app/oj/account/register.js b/static/src/js/app/oj/account/register.js index e6b4deb..b9d3468 100644 --- a/static/src/js/app/oj/account/register.js +++ b/static/src/js/app/oj/account/register.js @@ -2,6 +2,11 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf $("#register-form") .formValidation({ framework: "bootstrap", + icon: { + valid: 'glyphicon glyphicon-ok', + invalid: 'glyphicon glyphicon-remove', + validating: 'glyphicon glyphicon-refresh' + }, fields: { username: { validators: { @@ -50,6 +55,21 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf message: "两次输入的密码必须一致" } } + }, + email: { + validators: { + notEmpty: { + message: "请填写电子邮箱邮箱地址" + }, + emailAddress: { + message: "请填写有效的邮箱地址" + }, + remoteCSRF: { + message: "您已经注册过了", + url: "/api/email_check/", + field: 'email' + } + } } } } @@ -72,8 +92,6 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf bs_alert(data.data); } } - }) }); - }); \ No newline at end of file diff --git a/template/oj/account/register.html b/template/oj/account/register.html index a93014c..34dc9a4 100644 --- a/template/oj/account/register.html +++ b/template/oj/account/register.html @@ -14,6 +14,10 @@ +
+ + +
From c5446361db23f10e62257d2128baec6ec0966494 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 5 Aug 2015 19:45:35 +0800 Subject: [PATCH 018/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=20admin=20s?= =?UTF-8?q?pa=20=E7=9A=84=E9=80=9A=E7=94=A8=E4=BB=A3=E7=A0=81[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 4 +- static/src/js/app/admin/admin.js | 31 ++++++++ static/src/js/config.js | 1 + template/admin/admin.html | 122 +++++++++++++++++++++++++++++++ template/admin/index.html | 4 - template/admin/index/index.html | 1 + template/admin_base.html | 8 +- 7 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 static/src/js/app/admin/admin.js create mode 100644 template/admin/admin.html delete mode 100644 template/admin/index.html create mode 100644 template/admin/index/index.html diff --git a/oj/urls.py b/oj/urls.py index 0c8ea51..cc49f42 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 0000000..d3e30ac --- /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 3907d1c..079e45c 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 0000000..c712084 --- /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 df51a66..0000000 --- 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 0000000..6116828 --- /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 8525b2e..86cf7e2 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 %} From 5eacb9f3d74651cea6f8e6d57dcc246bd5478c11 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 5 Aug 2015 19:47:49 +0800 Subject: [PATCH 019/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20admin=20=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E8=BF=94=E5=9B=9E=E6=89=A7=E8=A1=8C=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E7=9A=84=20views=20=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/views.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/admin/views.py b/admin/views.py index 91ea44a..78762c0 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 From cbb86d72f1c7a5ab4030a34a8e4c1b9e8688097b Mon Sep 17 00:00:00 2001 From: sxw Date: Wed, 5 Aug 2015 20:01:15 +0800 Subject: [PATCH 020/304] =?UTF-8?q?=E6=9B=B4=E6=AD=A3=E4=BA=86validator?= =?UTF-8?q?=E5=91=BD=E5=90=8D=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8CremoteCSR?= =?UTF-8?q?F->remote.=E6=8A=8A=E5=8E=9F=E7=94=9F=E7=9A=84remote=E6=94=B9?= =?UTF-8?q?=E6=88=90=E4=BA=86remotex=E3=80=82=E3=80=82=E3=80=82=20[ci=20sk?= =?UTF-8?q?ip]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/oj/account/register.js | 8 +- static/src/js/config.js | 2 +- .../js/lib/formValidation/validator/remote.js | 144 +++-------------- .../formValidation/validator/remoteCSRF.js | 46 ------ .../lib/formValidation/validator/remotex.js | 146 ++++++++++++++++++ static/src/js/utils/validation.js | 2 +- 6 files changed, 175 insertions(+), 173 deletions(-) delete mode 100644 static/src/js/lib/formValidation/validator/remoteCSRF.js create mode 100644 static/src/js/lib/formValidation/validator/remotex.js diff --git a/static/src/js/app/oj/account/register.js b/static/src/js/app/oj/account/register.js index b9d3468..a7a3ea1 100644 --- a/static/src/js/app/oj/account/register.js +++ b/static/src/js/app/oj/account/register.js @@ -18,8 +18,10 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf max: 30, message: '用户名长度必须在3到30位之间' }, - usernameCheck:{ - message: '用户名已存在' + remote: { + message: "用户名已存在", + url: "/api/username_check/", + field: 'username' } } }, @@ -64,7 +66,7 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf emailAddress: { message: "请填写有效的邮箱地址" }, - remoteCSRF: { + remote: { message: "您已经注册过了", url: "/api/email_check/", field: 'email' diff --git a/static/src/js/config.js b/static/src/js/config.js index dc8b37c..67bb9b6 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -28,7 +28,7 @@ var require = { "validator/integer": "lib/formValidation/validator/integer", "validator/between": "lib/formValidation/validator/between", "validator/confirm":"lib/formValidation/validator/confirm", - "validator/remoteCSRF":"lib/formValidation/validator/remoteCSRF", + "validator/remote":"lib/formValidation/validator/remote", "validator/emailAddress":"lib/formValidation/validator/emailAddress", //富文本编辑器 不要直接使用,而是使用上面的editor simditor: "lib/simditor/simditor", diff --git a/static/src/js/lib/formValidation/validator/remote.js b/static/src/js/lib/formValidation/validator/remote.js index b0ba1af..e7f8d02 100755 --- a/static/src/js/lib/formValidation/validator/remote.js +++ b/static/src/js/lib/formValidation/validator/remote.js @@ -1,146 +1,46 @@ /** * remote validator - * - * @link http://formvalidation.io/validators/remote/ - * @author https://twitter.com/nghuuphuoc - * @copyright (c) 2013 - 2015 Nguyen Huu Phuoc - * @license http://formvalidation.io/license/ */ - (function(root, factory) { "use strict"; // AMD module is defined if (typeof define === "function" && define.amd) { - define("validator/remote", ["jquery", "base"], factory); + define("validator/remote", ["jquery", "base", "csrf"], factory); } else { // planted over the root! factory(root.jQuery, root.FormValidation); } - -}(this, function ($, FormValidation) { +}(this, function ($, FormValidation, csrfHeader) { FormValidation.I18n = $.extend(true, FormValidation.I18n || {}, { 'en_US': { remote: { - 'default': 'Please enter a valid value' + 'default': '' } } }); - FormValidation.Validator.remote = { - html5Attributes: { - message: 'message', - name: 'name', - type: 'type', - url: 'url', - data: 'data', - delay: 'delay' - }, - - /** - * Destroy the timer when destroying the bootstrapValidator (using validator.destroy() method) - */ - destroy: function(validator, $field, options) { - var ns = validator.getNamespace(), - timer = $field.data(ns + '.remote.timer'); - if (timer) { - clearTimeout(timer); - $field.removeData(ns + '.remote.timer'); - } - }, - - /** - * Request a remote server to check the input value - * - * @param {FormValidation.Base} validator Plugin instance - * @param {jQuery} $field Field element - * @param {Object} options Can consist of the following keys: - * - url {String|Function} - * - type {String} [optional] Can be GET or POST (default) - * - data {Object|Function} [optional]: By default, it will take the value - * { - * : - * } - * - delay - * - name {String} [optional]: Override the field name for the request. - * - message: The invalid message - * - headers: Additional headers - * @returns {Deferred} - */ validate: function(validator, $field, options) { - var ns = validator.getNamespace(), - value = validator.getFieldValue($field, 'remote'), - dfd = new $.Deferred(); - if (value === '') { - dfd.resolve($field, 'remote', { valid: true }); - return dfd; - } - - var name = $field.attr('data-' + ns + '-field'), - data = options.data || {}, - url = options.url, - type = options.type || 'GET', - headers = options.headers || {}; - - // Support dynamic data - if ('function' === typeof data) { - data = data.call(this, validator); - } - - // Parse string data from HTML5 attribute - if ('string' === typeof data) { - data = JSON.parse(data); - } - - // Support dynamic url - if ('function' === typeof url) { - url = url.call(this, validator); - } - - data[options.name || name] = value; - function runCallback() { - var xhr = $.ajax({ - type: type, - headers: headers, - url: url, - dataType: 'json', - data: data - }); - - xhr - .success(function(response) { - response.valid = response.valid === true || response.valid === 'true'; - dfd.resolve($field, 'remote', response); - }) - .error(function(response) { - dfd.resolve($field, 'remote', { - valid: false - }); - }); - - dfd.fail(function() { - xhr.abort(); - }); - - return dfd; - } - - if (options.delay) { - // Since the form might have multiple fields with the same name - // I have to attach the timer to the field element - if ($field.data(ns + '.remote.timer')) { - clearTimeout($field.data(ns + '.remote.timer')); - } - - $field.data(ns + '.remote.timer', setTimeout(runCallback, options.delay)); - return dfd; - } else { - return runCallback(); - } + var dfd = new $.Deferred(), ajaxData = {}; + ajaxData[options.field] = $field.val(); + if ($field.val() === '') + return true; + var url = options.url; + var xhr = $.ajax({ + beforeSend: csrfHeader, + url: url, + dataType: 'json', + data: ajaxData, + method: "post" + }); + xhr.success(function(response) { + dfd.resolve($field, 'remote',{valid:!response.data, message:options.msg}); + }) + .error(function(response) { + dfd.resolve($field, 'remote', {valid: false}); + }); + return dfd; } }; - - - return FormValidation.Validator.remote; })); diff --git a/static/src/js/lib/formValidation/validator/remoteCSRF.js b/static/src/js/lib/formValidation/validator/remoteCSRF.js deleted file mode 100644 index 9cab354..0000000 --- a/static/src/js/lib/formValidation/validator/remoteCSRF.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * remoteCSRF validator - */ -(function(root, factory) { - - "use strict"; - - // AMD module is defined - if (typeof define === "function" && define.amd) { - define("validator/remoteCSRF", ["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': { - remoteCSRF: { - 'default': '' - } - } - }); - FormValidation.Validator.remoteCSRF = { - validate: function(validator, $field, options) { - var dfd = new $.Deferred(), ajaxData = {}; - ajaxData[options.field] = $field.val(); - if ($field.val() === '') - return true; - var url = options.url; - var xhr = $.ajax({ - beforeSend: csrfHeader, - url: url, - dataType: 'json', - data: ajaxData, - method: "post" - }); - xhr.success(function(response) { - dfd.resolve($field, 'remoteCSRF',{valid:!response.data, message:options.msg}); - }) - .error(function(response) { - dfd.resolve($field, 'remoteCSRF', {valid: false}); - }); - return dfd; - } - }; -})); diff --git a/static/src/js/lib/formValidation/validator/remotex.js b/static/src/js/lib/formValidation/validator/remotex.js new file mode 100644 index 0000000..b0ba1af --- /dev/null +++ b/static/src/js/lib/formValidation/validator/remotex.js @@ -0,0 +1,146 @@ +/** + * remote validator + * + * @link http://formvalidation.io/validators/remote/ + * @author https://twitter.com/nghuuphuoc + * @copyright (c) 2013 - 2015 Nguyen Huu Phuoc + * @license http://formvalidation.io/license/ + */ + +(function(root, factory) { + + "use strict"; + + // AMD module is defined + if (typeof define === "function" && define.amd) { + define("validator/remote", ["jquery", "base"], factory); + } else { + // planted over the root! + factory(root.jQuery, root.FormValidation); + } + +}(this, function ($, FormValidation) { + FormValidation.I18n = $.extend(true, FormValidation.I18n || {}, { + 'en_US': { + remote: { + 'default': 'Please enter a valid value' + } + } + }); + + FormValidation.Validator.remote = { + html5Attributes: { + message: 'message', + name: 'name', + type: 'type', + url: 'url', + data: 'data', + delay: 'delay' + }, + + /** + * Destroy the timer when destroying the bootstrapValidator (using validator.destroy() method) + */ + destroy: function(validator, $field, options) { + var ns = validator.getNamespace(), + timer = $field.data(ns + '.remote.timer'); + if (timer) { + clearTimeout(timer); + $field.removeData(ns + '.remote.timer'); + } + }, + + /** + * Request a remote server to check the input value + * + * @param {FormValidation.Base} validator Plugin instance + * @param {jQuery} $field Field element + * @param {Object} options Can consist of the following keys: + * - url {String|Function} + * - type {String} [optional] Can be GET or POST (default) + * - data {Object|Function} [optional]: By default, it will take the value + * { + * : + * } + * - delay + * - name {String} [optional]: Override the field name for the request. + * - message: The invalid message + * - headers: Additional headers + * @returns {Deferred} + */ + validate: function(validator, $field, options) { + var ns = validator.getNamespace(), + value = validator.getFieldValue($field, 'remote'), + dfd = new $.Deferred(); + if (value === '') { + dfd.resolve($field, 'remote', { valid: true }); + return dfd; + } + + var name = $field.attr('data-' + ns + '-field'), + data = options.data || {}, + url = options.url, + type = options.type || 'GET', + headers = options.headers || {}; + + // Support dynamic data + if ('function' === typeof data) { + data = data.call(this, validator); + } + + // Parse string data from HTML5 attribute + if ('string' === typeof data) { + data = JSON.parse(data); + } + + // Support dynamic url + if ('function' === typeof url) { + url = url.call(this, validator); + } + + data[options.name || name] = value; + function runCallback() { + var xhr = $.ajax({ + type: type, + headers: headers, + url: url, + dataType: 'json', + data: data + }); + + xhr + .success(function(response) { + response.valid = response.valid === true || response.valid === 'true'; + dfd.resolve($field, 'remote', response); + }) + .error(function(response) { + dfd.resolve($field, 'remote', { + valid: false + }); + }); + + dfd.fail(function() { + xhr.abort(); + }); + + return dfd; + } + + if (options.delay) { + // Since the form might have multiple fields with the same name + // I have to attach the timer to the field element + if ($field.data(ns + '.remote.timer')) { + clearTimeout($field.data(ns + '.remote.timer')); + } + + $field.data(ns + '.remote.timer', setTimeout(runCallback, options.delay)); + return dfd; + } else { + return runCallback(); + } + } + }; + + + return FormValidation.Validator.remote; +})); diff --git a/static/src/js/utils/validation.js b/static/src/js/utils/validation.js index 45da8f5..191d585 100644 --- a/static/src/js/utils/validation.js +++ b/static/src/js/utils/validation.js @@ -9,7 +9,7 @@ define("validation", 'validator/integer', 'validator/between', 'validator/confirm', - 'validator/remoteCSRF', + 'validator/remote', 'validator/emailAddress'], function () { }); \ No newline at end of file From 117a6d3525b9a5e3dac0b0c6ce413445bec0af11 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Wed, 5 Aug 2015 20:11:25 +0800 Subject: [PATCH 021/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E4=BF=AE=E6=94=B9=E6=88=90=E5=8A=9F=E7=9A=84?= =?UTF-8?q?API=E6=B5=8B=E8=AF=95=EF=BC=8C=E4=BB=A5=E5=8F=8A=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E7=94=A8=E6=88=B7=E9=82=AE=E7=AE=B1=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=B9=B6=E5=81=9A=E4=BA=86=E7=9B=B8=E5=BA=94=E7=9A=84API?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=8C=E7=9B=AE=E5=89=8D=E7=9A=84account/v?= =?UTF-8?q?iew.py=E6=B5=8B=E8=AF=95=E8=A6=86=E7=9B=96=E7=8E=87=E4=B8=BA100?= =?UTF-8?q?%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/migrations/0003_user_email.py | 19 +++++++++++ account/models.py | 2 ++ account/serializers.py | 5 +++ account/tests.py | 48 ++++++++++++++++++++++++--- account/views.py | 29 ++++++++++++++-- oj/urls.py | 4 ++- 6 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 account/migrations/0003_user_email.py diff --git a/account/migrations/0003_user_email.py b/account/migrations/0003_user_email.py new file mode 100644 index 0000000..780afc3 --- /dev/null +++ b/account/migrations/0003_user_email.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0002_auto_20150731_2310'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='email', + field=models.EmailField(max_length=254, null=True, blank=True), + ), + ] diff --git a/account/models.py b/account/models.py index 1b8636a..1f02eef 100644 --- a/account/models.py +++ b/account/models.py @@ -19,6 +19,8 @@ class User(AbstractBaseUser): username = models.CharField(max_length=30, unique=True) # 真实姓名 real_name = models.CharField(max_length=30, blank=True, null=True) + # 用户邮箱 + email = models.EmailField(max_length=254, blank=True, null=True) admin_group = models.ForeignKey(AdminGroup, null=True, on_delete=models.SET_NULL) USERNAME_FIELD = 'username' diff --git a/account/serializers.py b/account/serializers.py index 41634b7..209e490 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -11,10 +11,15 @@ class UsernameCheckSerializer(serializers.Serializer): username = serializers.CharField(max_length=30) +class EmailCheckSerializer(serializers.Serializer): + email = serializers.EmailField(max_length=254) + + class UserRegisterSerializer(serializers.Serializer): username = serializers.CharField(max_length=30) real_name = serializers.CharField(max_length=30) password = serializers.CharField(max_length=30, min_length=6) + email = serializers.EmailField(max_length=254) class UserChangePasswordSerializer(serializers.Serializer): diff --git a/account/tests.py b/account/tests.py index 83226b7..76fbec6 100644 --- a/account/tests.py +++ b/account/tests.py @@ -1,6 +1,8 @@ # coding=utf-8 import json +from django.contrib import auth + from django.core.urlresolvers import reverse from django.test import TestCase, Client from django.http import HttpResponse @@ -63,6 +65,26 @@ class UsernameCheckTest(APITestCase): self.assertEqual(response.data, {"code": 0, "data": False}) +class EmailCheckTest(APITestCase): + def setUp(self): + self.client = APIClient() + self.url = reverse("email_check_api") + User.objects.create(email="11@qq.com") + + def test_invalid_data(self): + response = self.client.post(self.url, data={"email000": "11@qq.com"}) + self.assertEqual(response.data["code"], 1) + self.assertEqual(response.data["code"], 1) + + def test_email_exists(self): + response = self.client.post(self.url, data={"email": "11@qq.com"}) + self.assertEqual(response.data, {"code": 0, "data": True}) + + def test_email_does_not_exist(self): + response = self.client.post(self.url, data={"email": "33@qq.com"}) + self.assertEqual(response.data, {"code": 0, "data": False}) + + class UserRegisterAPITest(APITestCase): def setUp(self): self.client = APIClient() @@ -74,22 +96,35 @@ class UserRegisterAPITest(APITestCase): self.assertEqual(response.data["code"], 1) def test_short_password(self): - data = {"username": "test", "real_name": "TT", "password": "qq"} + data = {"username": "test", "real_name": "TT", "password": "qq", "email": "6060@qq.com"} response = self.client.post(self.url, data=data) self.assertEqual(response.data["code"], 1) def test_same_username(self): - User.objects.create(username="aa", real_name="ww") - data = {"username": "aa", "real_name": "ww", "password": "zzzzzzz"} + User.objects.create(username="aa") + data = {"username": "aa", "real_name": "ww", "password": "zzzzzzz", "email": "6060@qq.com"} response = self.client.post(self.url, data=data) self.assertEqual(response.data, {"code": 1, "data": u"用户名已存在"}) + def test_same_email(self): + User.objects.create(username="bb", email="8080@qq.com") + data = {"username": "aa", "real_name": "ww", "password": "zzzzzzz", "email": "8080@qq.com"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"该邮箱已被注册,请换其他邮箱进行注册"}) + + def test_success_email(self): + data = {"username": "cc", "real_name": "dd", "password": "xxxxxx", "email": "9090@qq.com"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 0, "data": u"注册成功!"}) + class UserChangePasswordAPITest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse("user_change_password_api") - User.objects.create(username="test", password="aaabbb") + user = User.objects.create(username="test") + user.set_password("aaabbb") + user.save() def test_error_old_password(self): data = {"username": "test", "old_password": "aaaccc", "new_password": "aaaddd"} @@ -106,6 +141,11 @@ class UserChangePasswordAPITest(APITestCase): response = self.client.post(self.url, data=data) self.assertEqual(response.data["code"], 1) + def test_success_change_password(self): + data = {"username": "test", "old_password": "aaabbb", "new_password": "aaaccc"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 0, "data": u"用户密码修改成功!"}) + @login_required def login_required_FBV_test_without_args(request): diff --git a/account/views.py b/account/views.py index dd05874..ea13663 100644 --- a/account/views.py +++ b/account/views.py @@ -7,7 +7,7 @@ from utils.shortcuts import serializer_invalid_response, error_response, success from .models import User from .serializers import UserLoginSerializer, UsernameCheckSerializer, UserRegisterSerializer, \ - UserChangePasswordSerializer + UserChangePasswordSerializer, EmailCheckSerializer class UserLoginAPIView(APIView): @@ -45,7 +45,13 @@ class UserRegisterAPIView(APIView): User.objects.get(username=data["username"]) return error_response(u"用户名已存在") except User.DoesNotExist: - user = User.objects.create(username=data["username"], real_name=data["real_name"]) + pass + try: + User.objects.get(email=data["email"]) + return error_response(u"该邮箱已被注册,请换其他邮箱进行注册") + except User.DoesNotExist: + user = User.objects.create(username=data["username"], real_name=data["real_name"], + email=data["email"]) user.set_password(data["password"]) user.save() return success_response(u"注册成功!") @@ -89,4 +95,21 @@ class UsernameCheckAPIView(APIView): except User.DoesNotExist: return success_response(False) else: - return serializer_invalid_response(serializer) \ No newline at end of file + return serializer_invalid_response(serializer) + +class EmailCheckAPIView(APIView): + def post(self, request): + """ + 检测邮箱是否存在,存在返回True,不存在返回False + --- + request_serializer: EmailCheckSerializer + """ + serializer = EmailCheckSerializer(data=request.DATA) + if serializer.is_valid(): + try: + User.objects.get(email=serializer.data["email"]) + return success_response(True) + except User.DoesNotExist: + return success_response(False) + else: + return serializer_invalid_response(serializer) diff --git a/oj/urls.py b/oj/urls.py index 0c8ea51..b2b3f12 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -3,7 +3,8 @@ from django.conf.urls import include, url from django.contrib import admin from django.views.generic import TemplateView -from account.views import UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView +from account.views import UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView, \ + EmailCheckAPIView from announcement.views import AnnouncementAPIView urlpatterns = [ @@ -17,6 +18,7 @@ urlpatterns = [ url(r'^api/register/$', UserRegisterAPIView.as_view(), name="user_register_api"), url(r'^api/change_password/$', UserChangePasswordAPIView.as_view(), name="user_change_password_api"), 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/$', AnnouncementAPIView.as_view(), name="announcement_api"), url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), From 98f8b650110d71190f13f92696464f0751c19098 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Wed, 5 Aug 2015 20:23:42 +0800 Subject: [PATCH 022/304] =?UTF-8?q?=E5=9C=A8account/tests.py=E4=B8=AD?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/tests.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/account/tests.py b/account/tests.py index 76fbec6..89410da 100644 --- a/account/tests.py +++ b/account/tests.py @@ -1,8 +1,6 @@ # coding=utf-8 import json -from django.contrib import auth - from django.core.urlresolvers import reverse from django.test import TestCase, Client from django.http import HttpResponse @@ -74,7 +72,6 @@ class EmailCheckTest(APITestCase): def test_invalid_data(self): response = self.client.post(self.url, data={"email000": "11@qq.com"}) self.assertEqual(response.data["code"], 1) - self.assertEqual(response.data["code"], 1) def test_email_exists(self): response = self.client.post(self.url, data={"email": "11@qq.com"}) From 875e3b16717161b3a024a786dbd5f9aaf8fae73d Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 6 Aug 2015 09:43:14 +0800 Subject: [PATCH 023/304] =?UTF-8?q?=E5=B0=86=E6=B3=A8=E5=86=8C=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E7=94=A8=E6=88=B7=E5=90=8D=E5=92=8C=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E7=9A=84=E9=AA=8C=E8=AF=81=E6=94=B9=E4=B8=BA=E4=BA=86=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E6=A1=86=E5=A4=B1=E5=8E=BB=E7=84=A6=E7=82=B9=E7=9A=84?= =?UTF-8?q?=E6=97=B6=E5=80=99=E6=89=8D=E9=AA=8C=E8=AF=81=EF=BC=8C=E9=98=B2?= =?UTF-8?q?=E6=AD=A2=E5=8F=91=E9=80=81=E5=A4=A7=E9=87=8F=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E3=80=82=20=E5=A2=9E=E5=8A=A0=E6=B3=A8=E5=86=8C=E7=9A=84?= =?UTF-8?q?=E6=97=B6=E5=80=99=E5=90=91=E5=90=8E=E7=AB=AF=E5=8F=91=E9=80=81?= =?UTF-8?q?=E9=82=AE=E7=AE=B1=E5=AD=97=E6=AE=B5=E3=80=82=20=E6=95=B4?= =?UTF-8?q?=E7=90=86=20js=20=E4=BB=A3=E7=A0=81=E9=A3=8E=E6=A0=BC=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/js/app/oj/account/change_password.js | 24 +++++------ static/src/js/app/oj/account/login.js | 12 +++--- static/src/js/app/oj/account/register.js | 42 +++++++++---------- 3 files changed, 38 insertions(+), 40 deletions(-) diff --git a/static/src/js/app/oj/account/change_password.js b/static/src/js/app/oj/account/change_password.js index e3e907f..c76dd9d 100644 --- a/static/src/js/app/oj/account/change_password.js +++ b/static/src/js/app/oj/account/change_password.js @@ -1,4 +1,4 @@ -require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrfHeader){ +require(["jquery", "bs_alert", "csrf", "validation"], function ($, bs_alert, csrfHeader) { $("#change_password-form").formValidation({ framework: "bootstrap", fields: { @@ -13,22 +13,22 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf 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'); + onSuccess: function (e, data) { + data.fv.revalidateField('confirm_password'); } }, confirm_password: { @@ -40,27 +40,27 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf original: $("#new_password"), message: "两次输入的密码必须一致" } - }, + } } } } - ).on('success.form.fv', function(e) { + ).on('success.form.fv', function (e) { e.preventDefault(); var username = $("#username").val(); - var new_password = $("#new_password ").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}, + data: {username: username, new_password: new_password, old_password: password}, dataType: "json", method: "post", success: function (data) { - if(!data.code){ - window.location.href="/login/"; + if (!data.code) { + window.location.href = "/login/"; } - else{ + else { bs_alert(data.data); } } diff --git a/static/src/js/app/oj/account/login.js b/static/src/js/app/oj/account/login.js index da4416d..2ecebf7 100644 --- a/static/src/js/app/oj/account/login.js +++ b/static/src/js/app/oj/account/login.js @@ -1,6 +1,6 @@ -require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrfHeader){ +require(["jquery", "bs_alert", "csrf", "validation"], function ($, bs_alert, csrfHeader) { $("#login-form") - .formValidation({ + .formValidation({ framework: "bootstrap", fields: { username: { @@ -19,7 +19,7 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf } } } - ).on('success.form.fv', function(e) { + ).on('success.form.fv', function (e) { e.preventDefault(); var username = $("#username").val(); var password = $("#password").val(); @@ -30,10 +30,10 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf dataType: "json", method: "post", success: function (data) { - if(!data.code){ - window.location.href="/"; + if (!data.code) { + window.location.href = "/"; } - else{ + else { bs_alert(data.data); } } diff --git a/static/src/js/app/oj/account/register.js b/static/src/js/app/oj/account/register.js index a7a3ea1..52c3d36 100644 --- a/static/src/js/app/oj/account/register.js +++ b/static/src/js/app/oj/account/register.js @@ -1,14 +1,10 @@ -require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrfHeader){ +require(["jquery", "bs_alert", "csrf", "validation"], function ($, bs_alert, csrfHeader) { $("#register-form") - .formValidation({ + .formValidation({ framework: "bootstrap", - icon: { - valid: 'glyphicon glyphicon-ok', - invalid: 'glyphicon glyphicon-remove', - validating: 'glyphicon glyphicon-refresh' - }, fields: { username: { + trigger: 'blur', validators: { notEmpty: { message: "请填写用户名" @@ -29,23 +25,23 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf validators: { notEmpty: { message: "请填写密码" - }, - stringLength: { - min: 6, - max: 30, - message: '密码长度必须在6到30位之间' - } + }, + stringLength: { + min: 6, + max: 30, + message: '密码长度必须在6到30位之间' + } }, - onSuccess: function(e, data) { - data.fv.revalidateField('confirm_password'); + onSuccess: function (e, data) { + data.fv.revalidateField('confirm_password'); } }, real_name: { validators: { notEmpty: { message: "请填写真实姓名" - } - }, + } + } }, confirm_password: { validators: { @@ -59,6 +55,7 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf } }, email: { + trigger: 'blur', validators: { notEmpty: { message: "请填写电子邮箱邮箱地址" @@ -75,22 +72,23 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf } } } - ).on('success.form.fv', function(e) { + ).on('success.form.fv', function (e) { e.preventDefault(); var username = $("#username").val(); var real_name = $("#real_name").val(); var password = $("#password").val(); + var email = $("#email").val(); $.ajax({ beforeSend: csrfHeader, url: "/api/register/", - data: {username: username, real_name: real_name, password: password}, + data: {username: username, real_name: real_name, password: password, email: email}, dataType: "json", method: "post", success: function (data) { - if(!data.code){ - window.location.href="/login/"; + if (!data.code) { + window.location.href = "/login/"; } - else{ + else { bs_alert(data.data); } } From 2bf0389e7be0285288709fc53aa0a22519223642 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Thu, 6 Aug 2015 11:08:02 +0800 Subject: [PATCH 024/304] =?UTF-8?q?announcement=20model.py=20=E9=87=8C?= =?UTF-8?q?=E9=9D=A2=E7=9A=84=20description=20=E6=94=B9=E5=90=8D=E4=B8=BA?= =?UTF-8?q?=20content,=E5=B9=B6=E4=BF=AE=E6=94=B9=E5=85=B6=E5=AF=B9?= =?UTF-8?q?=E5=BA=94=E7=9A=84serializers=E5=92=8Cviews?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0002_auto_20150806_1104.py | 19 +++++++++++++++++++ announcement/models.py | 4 ++-- announcement/serializers.py | 2 +- announcement/views.py | 2 +- 4 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 announcement/migrations/0002_auto_20150806_1104.py diff --git a/announcement/migrations/0002_auto_20150806_1104.py b/announcement/migrations/0002_auto_20150806_1104.py new file mode 100644 index 0000000..2768cc6 --- /dev/null +++ b/announcement/migrations/0002_auto_20150806_1104.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('announcement', '0001_initial'), + ] + + operations = [ + migrations.RenameField( + model_name='announcement', + old_name='description', + new_name='content', + ), + ] diff --git a/announcement/models.py b/announcement/models.py index a47eba6..a3574b8 100644 --- a/announcement/models.py +++ b/announcement/models.py @@ -7,8 +7,8 @@ from account.models import User class Announcement(models.Model): # 标题 title = models.CharField(max_length=50) - # 公告的描述 HTML 格式 - description = models.TextField() + # 公告的内容 HTML 格式 + content = models.TextField() # 创建时间 create_time = models.DateTimeField(auto_now_add=True) # 这个公告是谁创建的 diff --git a/announcement/serializers.py b/announcement/serializers.py index a1525b9..3878e14 100644 --- a/announcement/serializers.py +++ b/announcement/serializers.py @@ -4,5 +4,5 @@ from rest_framework import serializers class AnnouncementSerializer(serializers.Serializer): title = serializers.CharField(max_length=50) - description = serializers.CharField(max_length=10000) + content = serializers.CharField(max_length=10000) diff --git a/announcement/views.py b/announcement/views.py index 220e806..e3766e8 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -21,7 +21,7 @@ class AnnouncementAPIView(APIView): if serializer.is_valid(): data = serializer.data Announcement.objects.create(title=data["title"], - description=data["description"], + content=data["content"], created_by=request.user) return success_response(u"公告发布成功!") else: From 62f07e713f8a4137b24a6791cf1a18dbe1c06f82 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 6 Aug 2015 12:25:16 +0800 Subject: [PATCH 025/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20@admin=5Frequired?= =?UTF-8?q?=20=E4=BF=AE=E9=A5=B0=E7=AC=A6=E4=BB=A3=E7=A0=81=E5=92=8C?= =?UTF-8?q?=E5=AF=B9=E5=BA=94=E7=9A=84=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/decorators.py | 12 +++++- account/models.py | 3 +- account/test_urls.py | 16 +++++--- account/tests.py | 92 ++++++++++++++++++++++++++++++++++++++----- 4 files changed, 106 insertions(+), 17 deletions(-) diff --git a/account/decorators.py b/account/decorators.py index 6b10896..01cdd68 100644 --- a/account/decorators.py +++ b/account/decorators.py @@ -20,5 +20,13 @@ def login_required(func): return check -def admin_required(): - pass +def admin_required(func): + def check(*args, **kwargs): + request = args[-1] + if request.user.is_authenticated() and request.user.admin_type: + return func(*args, **kwargs) + if request.is_ajax(): + return error_response(u"需要管理员权限") + else: + return render(request, "utils/error.html", {"error": "需要管理员权限"}) + return check diff --git a/account/models.py b/account/models.py index 1f02eef..7c5afb9 100644 --- a/account/models.py +++ b/account/models.py @@ -21,7 +21,8 @@ class User(AbstractBaseUser): real_name = models.CharField(max_length=30, blank=True, null=True) # 用户邮箱 email = models.EmailField(max_length=254, blank=True, null=True) - admin_group = models.ForeignKey(AdminGroup, null=True, on_delete=models.SET_NULL) + # 0代表不是管理员 1是普通管理员 2是超级管理员 + admin_type = models.IntegerField(default=0) USERNAME_FIELD = 'username' REQUIRED_FIELDS = [] diff --git a/account/test_urls.py b/account/test_urls.py index 4066721..9757d90 100644 --- a/account/test_urls.py +++ b/account/test_urls.py @@ -1,12 +1,18 @@ # coding=utf-8 from django.conf.urls import include, url -from .tests import LoginRequiredCBVTestWithArgs, LoginRequiredCBVTestWithoutArgs +from .tests import (LoginRequiredCBVTestWithArgs, LoginRequiredCBVTestWithoutArgs, + AdminRequiredCBVTestWithArgs, AdminRequiredCBVTestWithoutArgs) urlpatterns = [ - url(r'^test/fbv/1/$', "account.tests.login_required_FBV_test_without_args"), - url(r'^test/fbv/(?P\d+)/$', "account.tests.login_required_FBC_test_with_args"), - url(r'^test/cbv/1/$', LoginRequiredCBVTestWithoutArgs.as_view()), - url(r'^test/cbv/(?P\d+)/$', LoginRequiredCBVTestWithArgs.as_view()), + url(r'^login_required_test/fbv/1/$', "account.tests.login_required_FBV_test_without_args"), + url(r'^login_required_test/fbv/(?P\d+)/$', "account.tests.login_required_FBC_test_with_args"), + url(r'^login_required_test/cbv/1/$', LoginRequiredCBVTestWithoutArgs.as_view()), + url(r'^login_required_test/cbv/(?P\d+)/$', LoginRequiredCBVTestWithArgs.as_view()), + + url(r'^admin_required_test/fbv/1/$', "account.tests.admin_required_FBV_test_without_args"), + url(r'^admin_required_test/fbv/(?P\d+)/$', "account.tests.admin_required_FBC_test_with_args"), + url(r'^admin_required_test/cbv/1/$', AdminRequiredCBVTestWithoutArgs.as_view()), + url(r'^admin_required_test/cbv/(?P\d+)/$', AdminRequiredCBVTestWithArgs.as_view()), ] diff --git a/account/tests.py b/account/tests.py index 89410da..3529bb6 100644 --- a/account/tests.py +++ b/account/tests.py @@ -10,7 +10,7 @@ from rest_framework.views import APIView from rest_framework.response import Response from .models import User -from .decorators import login_required +from .decorators import login_required, admin_required class UserLoginTest(TestCase): @@ -159,6 +159,7 @@ class LoginRequiredCBVTestWithoutArgs(APIView): def get(self, request): return HttpResponse("class based view login required test1") + class LoginRequiredCBVTestWithArgs(APIView): @login_required def get(self, request, problem_id): @@ -176,40 +177,113 @@ class LoginRequiredDecoratorTest(TestCase): def test_fbv_without_args(self): # 没登陆 - response = self.client.get("/test/fbv/1/") + response = self.client.get("/login_required_test/fbv/1/") self.assertTemplateUsed(response, "utils/error.html") # 登陆后 self.client.login(username="test", password="test") - response = self.client.get("/test/fbv/1/") + response = self.client.get("/login_required_test/fbv/1/") self.assertEqual(response.content, "function based view test1") def test_fbv_with_args(self): # 没登陆 - response = self.client.get("/test/fbv/1024/") + response = self.client.get("/login_required_test/fbv/1024/") self.assertTemplateUsed(response, "utils/error.html") # 登陆后 self.client.login(username="test", password="test") - response = self.client.get("/test/fbv/1024/") + response = self.client.get("/login_required_test/fbv/1024/") self.assertEqual(response.content, "1024") def test_cbv_without_args(self): # 没登陆 - response = self.client.get("/test/cbv/1/") + response = self.client.get("/login_required_test/cbv/1/") self.assertTemplateUsed(response, "utils/error.html") # 登陆后 self.client.login(username="test", password="test") - response = self.client.get("/test/cbv/1/") + response = self.client.get("/login_required_test/cbv/1/") self.assertEqual(response.content, "class based view login required test1") def test_cbv_with_args(self): # 没登陆 - response = self.client.get("/test/cbv/1024/", HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = self.client.get("/login_required_test/cbv/1024/", HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(json.loads(response.content), {"code": 1, "data": u"请先登录"}) # 登陆后 self.client.login(username="test", password="test") - response = self.client.get("/test/cbv/1024/") + response = self.client.get("/login_required_test/cbv/1024/") + self.assertEqual(response.content, "1024") + + +@admin_required +def admin_required_FBV_test_without_args(request): + return HttpResponse("function based view test1") + + +@admin_required +def admin_required_FBC_test_with_args(request, problem_id): + return HttpResponse(problem_id) + + +class AdminRequiredCBVTestWithoutArgs(APIView): + @admin_required + def get(self, request): + return HttpResponse("class based view login required test1") + + +class AdminRequiredCBVTestWithArgs(APIView): + @admin_required + def get(self, request, problem_id): + return HttpResponse(problem_id) + + +class AdminRequiredDecoratorTest(TestCase): + urls = 'account.test_urls' + + def setUp(self): + self.client = Client() + user = User.objects.create(username="test") + user.admin_type = 1 + user.set_password("test") + user.save() + + def test_fbv_without_args(self): + # 没登陆 + response = self.client.get("/admin_required_test/fbv/1/") + self.assertTemplateUsed(response, "utils/error.html") + + # 登陆后 + self.client.login(username="test", password="test") + response = self.client.get("/admin_required_test/fbv/1/") + self.assertEqual(response.content, "function based view test1") + + def test_fbv_with_args(self): + # 没登陆 + response = self.client.get("/admin_required_test/fbv/1024/") + self.assertTemplateUsed(response, "utils/error.html") + + # 登陆后 + self.client.login(username="test", password="test") + response = self.client.get("/admin_required_test/fbv/1024/") + self.assertEqual(response.content, "1024") + + def test_cbv_without_args(self): + # 没登陆 + response = self.client.get("/admin_required_test/cbv/1/") + self.assertTemplateUsed(response, "utils/error.html") + + # 登陆后 + self.client.login(username="test", password="test") + response = self.client.get("/admin_required_test/cbv/1/") + self.assertEqual(response.content, "class based view login required test1") + + def test_cbv_with_args(self): + # 没登陆 + response = self.client.get("/admin_required_test/cbv/1024/", HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(json.loads(response.content), {"code": 1, "data": u"需要管理员权限"}) + + # 登陆后 + self.client.login(username="test", password="test") + response = self.client.get("/admin_required_test/cbv/1024/") self.assertEqual(response.content, "1024") From 53afd2a0324281115e46607ff6f1572e1d922a03 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 6 Aug 2015 13:05:00 +0800 Subject: [PATCH 026/304] =?UTF-8?q?bug-fix:=20=E5=A2=9E=E5=8A=A0=20account?= =?UTF-8?q?/models=20=E7=9A=84=20migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/migrations/0004_auto_20150806_1211.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 account/migrations/0004_auto_20150806_1211.py diff --git a/account/migrations/0004_auto_20150806_1211.py b/account/migrations/0004_auto_20150806_1211.py new file mode 100644 index 0000000..d3d2225 --- /dev/null +++ b/account/migrations/0004_auto_20150806_1211.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0003_user_email'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='admin_group', + ), + migrations.AddField( + model_name='user', + name='admin_type', + field=models.IntegerField(default=0), + ), + ] From 96c8d28c6460c6e208b47e8fd0b70f7f3beace25 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Thu, 6 Aug 2015 13:17:15 +0800 Subject: [PATCH 027/304] =?UTF-8?q?announcement=20model.py=20=E9=87=8C?= =?UTF-8?q?=E9=9D=A2=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA=E6=96=B0=E7=9A=84?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=EF=BC=88=E6=9C=80=E5=90=8E=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=97=B6=E9=97=B4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/migrations/0004_auto_20150806_1251.py | 23 +++++++++++++++++++ announcement/migrations/0001_initial.py | 3 ++- .../migrations/0002_auto_20150806_1104.py | 19 --------------- announcement/models.py | 2 ++ 4 files changed, 27 insertions(+), 20 deletions(-) create mode 100644 account/migrations/0004_auto_20150806_1251.py delete mode 100644 announcement/migrations/0002_auto_20150806_1104.py diff --git a/account/migrations/0004_auto_20150806_1251.py b/account/migrations/0004_auto_20150806_1251.py new file mode 100644 index 0000000..d3d2225 --- /dev/null +++ b/account/migrations/0004_auto_20150806_1251.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0003_user_email'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='admin_group', + ), + migrations.AddField( + model_name='user', + name='admin_type', + field=models.IntegerField(default=0), + ), + ] diff --git a/announcement/migrations/0001_initial.py b/announcement/migrations/0001_initial.py index 24a1d13..7e8d583 100644 --- a/announcement/migrations/0001_initial.py +++ b/announcement/migrations/0001_initial.py @@ -17,8 +17,9 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('title', models.CharField(max_length=50)), - ('description', models.TextField()), + ('content', models.TextField()), ('create_time', models.DateTimeField(auto_now_add=True)), + ('last_update_time', models.DateTimeField(auto_now=True)), ('visible', models.BooleanField(default=True)), ('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)), ], diff --git a/announcement/migrations/0002_auto_20150806_1104.py b/announcement/migrations/0002_auto_20150806_1104.py deleted file mode 100644 index 2768cc6..0000000 --- a/announcement/migrations/0002_auto_20150806_1104.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('announcement', '0001_initial'), - ] - - operations = [ - migrations.RenameField( - model_name='announcement', - old_name='description', - new_name='content', - ), - ] diff --git a/announcement/models.py b/announcement/models.py index a3574b8..491c219 100644 --- a/announcement/models.py +++ b/announcement/models.py @@ -13,6 +13,8 @@ class Announcement(models.Model): create_time = models.DateTimeField(auto_now_add=True) # 这个公告是谁创建的 created_by = models.ForeignKey(User) + # 最后更新时间 + last_update_time = models.DateTimeField(auto_now=True) # 是否可见 false的话相当于删除 visible = models.BooleanField(default=True) From 65dfd1149b6984c10cd273f314a9aa6cbf7181a6 Mon Sep 17 00:00:00 2001 From: sxw Date: Thu, 6 Aug 2015 15:12:28 +0800 Subject: [PATCH 028/304] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E5=85=B3?= =?UTF-8?q?=E4=BA=8E=E7=94=B5=E5=AD=90=E9=82=AE=E7=AE=B1=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E5=AD=98=E5=9C=A8=E5=89=8D=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=A0=87=E5=87=86=E5=B7=AE=E5=BC=82=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=89=8D=E7=AB=AF=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E5=85=88=E5=88=A4=E6=96=ADcode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/lib/formValidation/validator/remote.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/static/src/js/lib/formValidation/validator/remote.js b/static/src/js/lib/formValidation/validator/remote.js index e7f8d02..83a6fc5 100755 --- a/static/src/js/lib/formValidation/validator/remote.js +++ b/static/src/js/lib/formValidation/validator/remote.js @@ -35,6 +35,8 @@ method: "post" }); xhr.success(function(response) { + if (response.code == 1) + dfd.resolve($field, 'remote',{valid:true, message:options.msg}); dfd.resolve($field, 'remote',{valid:!response.data, message:options.msg}); }) .error(function(response) { From 844e9702764c36d60bd0dac9ba0f1f944f96c51a Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Thu, 6 Aug 2015 16:28:59 +0800 Subject: [PATCH 029/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=85=AC?= =?UTF-8?q?=E5=91=8A=E5=8F=91=E5=B8=83=E6=88=90=E5=8A=9F=E7=9A=84=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=EF=BC=8C=E4=BB=A5=E5=8F=8A=E5=85=AC=E5=91=8A=E5=88=86?= =?UTF-8?q?=E9=A1=B5=E7=9A=84APIview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- announcement/serializers.py | 18 +++++++++++++++++- announcement/tests.py | 15 +++++++++++++-- announcement/views.py | 24 +++++++++++++++++------- oj/urls.py | 6 ++++-- 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/announcement/serializers.py b/announcement/serializers.py index 3878e14..a195b2e 100644 --- a/announcement/serializers.py +++ b/announcement/serializers.py @@ -1,8 +1,24 @@ # coding=utf-8 from rest_framework import serializers +from account.models import User +from .models import Announcement -class AnnouncementSerializer(serializers.Serializer): + +class CreateAnnouncementSerializer(serializers.Serializer): title = serializers.CharField(max_length=50) content = serializers.CharField(max_length=10000) + +class AnnouncementSerializer(serializers.ModelSerializer): + + class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ["username"] + + created_by = UserSerializer() + + class Meta: + model = Announcement + diff --git a/announcement/tests.py b/announcement/tests.py index f73805e..f337a9c 100644 --- a/announcement/tests.py +++ b/announcement/tests.py @@ -3,14 +3,25 @@ from django.core.urlresolvers import reverse from rest_framework.test import APITestCase, APIClient +from account.models import User + class AnnouncementAPITest(APITestCase): def setUp(self): self.client = APIClient() - self.url = reverse("announcement_api") + self.url = reverse("announcement_admin_api") + user = User.objects.create(username="test") + user.set_password("test") + user.save() def test_invalid_format(self): - # todo 判断用户是否登录 + self.client.login(username="test", password="test") 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="test") + data = {"title": "title0", "content": "content0"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 0, "data": u"公告发布成功!"}) \ No newline at end of file diff --git a/announcement/views.py b/announcement/views.py index e3766e8..b01af7f 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -4,20 +4,19 @@ from rest_framework.views import APIView 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 AnnouncementSerializer +from .serializers import CreateAnnouncementSerializer, AnnouncementSerializer -class AnnouncementAPIView(APIView): - # todo 判断用户是否需要登录 +class AnnouncementAdminAPIView(APIView): def post(self, request): """ 公告发布json api接口 --- - request_serializer: AnnouncementSerializer + request_serializer: CreateAnnouncementSerializer """ - serializer = AnnouncementSerializer(data=request.DATA) + serializer = CreateAnnouncementSerializer(data=request.DATA) if serializer.is_valid(): data = serializer.data Announcement.objects.create(title=data["title"], @@ -25,4 +24,15 @@ class AnnouncementAPIView(APIView): created_by=request.user) return success_response(u"公告发布成功!") else: - return serializer_invalid_response(serializer) \ No newline at end of file + return serializer_invalid_response(serializer) + + +class AnnouncementAPIView(APIView): + def get(self, request): + """ + 公告分页json api接口 + --- + request_serializer: AnnouncementSerializer + """ + announcement = Announcement.objects.all().order_by("last_update_time") + return paginate(request, announcement, AnnouncementSerializer) diff --git a/oj/urls.py b/oj/urls.py index 82ecafb..2586cb6 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -5,7 +5,7 @@ from django.views.generic import TemplateView from account.views import UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView, \ EmailCheckAPIView -from announcement.views import AnnouncementAPIView +from announcement.views import AnnouncementAPIView, AnnouncementAdminAPIView from admin.views import AdminTemplateView urlpatterns = [ @@ -20,9 +20,11 @@ urlpatterns = [ url(r'^api/change_password/$', UserChangePasswordAPIView.as_view(), name="user_change_password_api"), 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/$', AnnouncementAPIView.as_view(), name="announcement_api"), + url(r'^api/admin/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"), url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), + url(r'^announcements/$', AnnouncementAPIView.as_view()), + 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") From 8e8909973ee8fbbca65829dbd50e9cbd504338f9 Mon Sep 17 00:00:00 2001 From: sxw Date: Thu, 6 Aug 2015 16:33:37 +0800 Subject: [PATCH 030/304] =?UTF-8?q?=E5=8F=96=E6=B6=88username=EF=BC=8Cemai?= =?UTF-8?q?l=E5=AD=97=E6=AE=B5=E5=A4=B1=E5=8E=BB=E7=84=A6=E7=82=B9?= =?UTF-8?q?=E5=86=8D=E5=8F=91=E9=80=81=E9=AA=8C=E8=AF=81=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/oj/account/register.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/static/src/js/app/oj/account/register.js b/static/src/js/app/oj/account/register.js index 52c3d36..432ce21 100644 --- a/static/src/js/app/oj/account/register.js +++ b/static/src/js/app/oj/account/register.js @@ -4,7 +4,6 @@ require(["jquery", "bs_alert", "csrf", "validation"], function ($, bs_alert, csr framework: "bootstrap", fields: { username: { - trigger: 'blur', validators: { notEmpty: { message: "请填写用户名" @@ -55,7 +54,6 @@ require(["jquery", "bs_alert", "csrf", "validation"], function ($, bs_alert, csr } }, email: { - trigger: 'blur', validators: { notEmpty: { message: "请填写电子邮箱邮箱地址" From e0dec79066d088b86e7fefb48c9a66ec213d55a6 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 6 Aug 2015 16:45:51 +0800 Subject: [PATCH 031/304] =?UTF-8?q?fix-bug=20=E4=BF=AE=E5=A4=8D=20account?= =?UTF-8?q?=20migrate=20=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/migrations/0004_auto_20150806_1251.py | 23 ------------------- ...806_1211.py => 0004_auto_20150806_1645.py} | 0 2 files changed, 23 deletions(-) delete mode 100644 account/migrations/0004_auto_20150806_1251.py rename account/migrations/{0004_auto_20150806_1211.py => 0004_auto_20150806_1645.py} (100%) diff --git a/account/migrations/0004_auto_20150806_1251.py b/account/migrations/0004_auto_20150806_1251.py deleted file mode 100644 index d3d2225..0000000 --- a/account/migrations/0004_auto_20150806_1251.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0003_user_email'), - ] - - operations = [ - migrations.RemoveField( - model_name='user', - name='admin_group', - ), - migrations.AddField( - model_name='user', - name='admin_type', - field=models.IntegerField(default=0), - ), - ] diff --git a/account/migrations/0004_auto_20150806_1211.py b/account/migrations/0004_auto_20150806_1645.py similarity index 100% rename from account/migrations/0004_auto_20150806_1211.py rename to account/migrations/0004_auto_20150806_1645.py From 529cea5476ab871045f8d3d0a22156d5a62df4c9 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 6 Aug 2015 16:49:48 +0800 Subject: [PATCH 032/304] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20announcement=20url?= =?UTF-8?q?=20=E5=89=8D=E7=BC=80=E9=94=99=E8=AF=AF=EF=BC=9B=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20api=20doc=20=E6=A0=BC=E5=BC=8F=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- announcement/views.py | 2 +- oj/urls.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/announcement/views.py b/announcement/views.py index b01af7f..9649875 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -32,7 +32,7 @@ class AnnouncementAPIView(APIView): """ 公告分页json api接口 --- - request_serializer: AnnouncementSerializer + response_serializer: AnnouncementSerializer """ announcement = Announcement.objects.all().order_by("last_update_time") return paginate(request, announcement, AnnouncementSerializer) diff --git a/oj/urls.py b/oj/urls.py index 2586cb6..97ecb02 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -23,7 +23,7 @@ urlpatterns = [ url(r'^api/admin/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"), url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), - url(r'^announcements/$', AnnouncementAPIView.as_view()), + url(r'^api/announcements/$', AnnouncementAPIView.as_view(), name="announcement_list_api"), 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"), From 1675eed67d73a0bf04daa9c0eacf806128ea6828 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Thu, 6 Aug 2015 19:07:46 +0800 Subject: [PATCH 033/304] =?UTF-8?q?=E6=B7=BB=E5=8A=A0Announcement=E7=9A=84?= =?UTF-8?q?API=E6=B5=8B=E8=AF=95=EF=BC=8C=E5=8D=B3url=E4=B8=AD=E7=9A=84ann?= =?UTF-8?q?ouncement=5Flist=5Fapi=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=85=AC?= =?UTF-8?q?=E5=91=8A=E7=BC=96=E8=BE=91=E5=8F=8A=E7=9B=B8=E5=BA=94=E7=9A=84?= =?UTF-8?q?API=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/views.py | 1 + announcement/serializers.py | 6 +++++ announcement/tests.py | 44 +++++++++++++++++++++++++++++++++++-- announcement/views.py | 25 ++++++++++++++++++++- 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/account/views.py b/account/views.py index ea13663..7214c8a 100644 --- a/account/views.py +++ b/account/views.py @@ -97,6 +97,7 @@ class UsernameCheckAPIView(APIView): else: return serializer_invalid_response(serializer) + class EmailCheckAPIView(APIView): def post(self, request): """ diff --git a/announcement/serializers.py b/announcement/serializers.py index a195b2e..d6380b0 100644 --- a/announcement/serializers.py +++ b/announcement/serializers.py @@ -22,3 +22,9 @@ class AnnouncementSerializer(serializers.ModelSerializer): class Meta: model = Announcement + +class EditAnnouncementSerializer(serializers.Serializer): + id = serializers.IntegerField() + title = serializers.CharField(max_length=50) + content = serializers.CharField(max_length=10000) + visible = serializers.BooleanField() diff --git a/announcement/tests.py b/announcement/tests.py index f337a9c..a7fd120 100644 --- a/announcement/tests.py +++ b/announcement/tests.py @@ -4,9 +4,10 @@ from django.core.urlresolvers import reverse from rest_framework.test import APITestCase, APIClient from account.models import User +from announcement.models import Announcement -class AnnouncementAPITest(APITestCase): +class AnnouncementAdminAPITest(APITestCase): def setUp(self): self.client = APIClient() self.url = reverse("announcement_admin_api") @@ -14,6 +15,7 @@ class AnnouncementAPITest(APITestCase): user.set_password("test") user.save() + # 以下是发布公告的测试 def test_invalid_format(self): self.client.login(username="test", password="test") data = {"title": "test1"} @@ -24,4 +26,42 @@ class AnnouncementAPITest(APITestCase): self.client.login(username="test", password="test") data = {"title": "title0", "content": "content0"} response = self.client.post(self.url, data=data) - self.assertEqual(response.data, {"code": 0, "data": u"公告发布成功!"}) \ No newline at end of file + self.assertEqual(response.data, {"code": 0, "data": u"公告发布成功!"}) + + def test_post_invalid_data(self): + self.client.login(username="test", password="test") + data = {"title": "test"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 1) + + # 以下是编辑公告的测试 + def test_put_invalid_data(self): + self.client.login(username="test", password="test") + 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): + 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} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"该公告不存在!"}) + + def test_success_edit_announcement(self): + 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} + 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") + + def test_success_get_data(self): + self.assertEqual(self.client.get(self.url).data["code"], 0) diff --git a/announcement/views.py b/announcement/views.py index 9649875..bc14889 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -6,7 +6,8 @@ from utils.shortcuts import serializer_invalid_response, error_response, success from account.models import User from utils.shortcuts import paginate from .models import Announcement -from .serializers import CreateAnnouncementSerializer, AnnouncementSerializer +from .serializers import (CreateAnnouncementSerializer, AnnouncementSerializer, + EditAnnouncementSerializer) class AnnouncementAdminAPIView(APIView): @@ -26,6 +27,28 @@ class AnnouncementAdminAPIView(APIView): else: return serializer_invalid_response(serializer) + def put(self, request): + """ + 公告编辑json api接口 + --- + request_serializer: EditAnnouncementSerializer + response_serializer: AnnouncementSerializer + """ + serializer = EditAnnouncementSerializer(data=request.DATA) + if serializer.is_valid(): + data = serializer.data + try: + announcement = Announcement.objects.get(id=data["id"]) + except Announcement.DoesNotExist: + return error_response(u"该公告不存在!") + announcement.title = data["title"] + announcement.content = data["content"] + announcement.visible = data["visible"] + announcement.save() + return success_response(AnnouncementSerializer(announcement).data) + else: + return serializer_invalid_response(serializer) + class AnnouncementAPIView(APIView): def get(self, request): From ab63ac652fc2ee838ce58e8caa1a6dd8ec3b66f7 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 6 Aug 2015 23:47:28 +0800 Subject: [PATCH 034/304] =?UTF-8?q?=E5=88=86=E7=A6=BB=E5=85=AC=E5=85=B1=20?= =?UTF-8?q?js=EF=BC=9B=E4=BF=AE=E6=94=B9=20footer=20=E6=95=88=E6=9E=9C?= =?UTF-8?q?=EF=BC=8C=E5=9B=BA=E5=AE=9A=E5=9C=A8=E5=BA=95=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/css/admin.css | 24 +++++++----------------- static/src/css/global.css | 15 +++++++++++++++ static/src/css/oj.css | 19 ------------------- 3 files changed, 22 insertions(+), 36 deletions(-) create mode 100644 static/src/css/global.css diff --git a/static/src/css/admin.css b/static/src/css/admin.css index 75fc901..e54f0e3 100644 --- a/static/src/css/admin.css +++ b/static/src/css/admin.css @@ -1,25 +1,15 @@ +@import url("global.css"); @import url("bootstrap/bootstrap.min.css"); @import url("bootstrap/todc-bootstrap.min.css"); @import url("codeMirror/codemirror.css"); @import url("simditor/simditor.css"); @import url("webuploader/webuploader.css"); @import url("datetime_picker/bootstrap-datetimepicker.css"); -html, body { - height: 100%; -} -img { - max-width: 100%; - height: auto; -} - -.footer { - padding-top: 30px; - padding-bottom: 30px; - float: bottom; - bottom: 0; -} - -label { - font-size: 16px; +#loading-gif{ + width: 40px; + height: 40px; + margin: auto; + position: absolute; + top: 0; left: 0; bottom: 0; right: 0; } \ No newline at end of file diff --git a/static/src/css/global.css b/static/src/css/global.css new file mode 100644 index 0000000..edc04a7 --- /dev/null +++ b/static/src/css/global.css @@ -0,0 +1,15 @@ +img { + max-width: 100%; + height: auto; +} + +.footer { + left: 0; + right: 0; + position: absolute; + bottom: 30px; +} + +label { + font-size: 16px; +} \ No newline at end of file diff --git a/static/src/css/oj.css b/static/src/css/oj.css index fcf80dd..5e1bfc5 100644 --- a/static/src/css/oj.css +++ b/static/src/css/oj.css @@ -1,25 +1,7 @@ @import url("bootstrap/bootstrap.min.css"); @import url("bootstrap/todc-bootstrap.min.css"); @import url("codeMirror/codemirror.css"); -html, body { - height: 100%; -} -img { - max-width: 100%; - height: auto; -} - -.footer { - padding-top: 30px; - padding-bottom: 30px; - float: bottom; - bottom: 0; -} - -label { - font-size: 16px; -} #language-selector { width: 130px; @@ -47,7 +29,6 @@ label { font-size: 15px; } -/* index css */ .jumbotron { text-align: center; background-color: transparent; From d6a36fe20ce722b89e06d8e81337626b557c4a89 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 6 Aug 2015 23:47:54 +0800 Subject: [PATCH 035/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E6=95=88=E6=9E=9C=E5=92=8C=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E7=9A=84=20js=20=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/admin/admin.js | 6 +++++- template/admin/admin.html | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/static/src/js/app/admin/admin.js b/static/src/js/app/admin/admin.js index d3e30ac..b44f575 100644 --- a/static/src/js/app/admin/admin.js +++ b/static/src/js/app/admin/admin.js @@ -20,12 +20,16 @@ define("admin", ["jquery", "avalon"], function($, avalon){ if(hash){ li_inactive(".list-group-item"); li_active("#li-" + hash); + $("#loading-gif").show(); vm.template_url = "template/index/" + hash + ".html"; } }; var vm = avalon.define({ $id: "admin", - template_url: "template/index/index.html" + template_url: "template/index/index.html", + hide_loading: function(){ + $("#loading-gif").hide(); + } }); }); \ No newline at end of file diff --git a/template/admin/admin.html b/template/admin/admin.html index c712084..6ec5a4e 100644 --- a/template/admin/admin.html +++ b/template/admin/admin.html @@ -81,9 +81,9 @@
- + -
+
From 2a29dfffb404ae146be7d34988aed1530d0d92fe Mon Sep 17 00:00:00 2001 From: sxw Date: Fri, 7 Aug 2015 09:24:52 +0800 Subject: [PATCH 036/304] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=85=AC=E5=91=8A?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=9A=84js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/app/admin/announcement/announcement.js | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 static/src/js/app/admin/announcement/announcement.js diff --git a/static/src/js/app/admin/announcement/announcement.js b/static/src/js/app/admin/announcement/announcement.js new file mode 100644 index 0000000..115873c --- /dev/null +++ b/static/src/js/app/admin/announcement/announcement.js @@ -0,0 +1,153 @@ +require(["jquery", "avalon", "csrf", "bs_alert", "editor", "validation"], function ($, avalon, csrfHeader, bs_alert, editor) { + annoumcementEditor = editor("#editor"); + editAnnoumcementEditor = null; + if (!avalon.vmodels.announcement) + avalon.vmodels.announcement = null; + vm = avalon.define({ + $id: "announcement", + announcement: [], + previous_page: 0, + next_page: 0, + page: 1, + isEditing: false, + getState: function (el) { + if (el.visible) + return "可见"; + else + return "隐藏"; + }, + getNext: function (el) { + getPageData(++(vm.page), function (data) { + }); + }, + getPrevious: function (el) { + getPageData(--(vm.page), function (data) { + }); + }, + getBtnClass: function (btn) { + if (btn) { + return vm.next_page ? "btn btn-primary" : "btn btn-primary disabled"; + } + else { + return vm.previous_page ? "btn btn-primary" : "btn btn-primary disabled"; + } + + }, + enEdit: function(el){ + $("#newTitle").val(el.title); + if (!editAnnoumcementEditor) + editAnnoumcementEditor = editor("#editAnnoumcementEditor"); + editAnnoumcementEditor.setValue(el.content); + vm.isEditing = true; + editAnnoumcementEditor.focus(); + } + }); + + getPageData(1, function (data) { + avalon.scan(); + }); + + function getPageData(page, callback) { + $.ajax({ + beforeSend: csrfHeader, + url: "/api/announcements/?paging=true&page=" + page + "&page_size=10", + dataType: "json", + method: "get", + success: function (data) { + if (!data.code) { + vm.announcement = data.data.results; + vm.previous_page = data.data.previous_page; + vm.next_page = data.data.next_page; + callback(data); + } + else { + bs_alert(data.data); + } + } + }); + } + + + $("#announcement-edit-form") + .formValidation({ + framework: "bootstrap", + fields: { + title: { + validators: { + notEmpty: { + message: "请填写公告标题" + } + } + } + } + } + ).on('success.form.fv', function (e) { + e.preventDefault(); + var title = $("#newTitle").val(); + var content = editor1.getValue(); + if (content == "") { + bs_alert("请填写公告内容") + return; + } + $.ajax({ + beforeSend: csrfHeader, + url: "/api/admin/announcement/", + data: {title: title, content: content}, + dataType: "json", + method: "post", + success: function (data) { + if (!data.code) { + bs_alert("提交成功!"); + $("#title").val(""); + editor1.setValue(""); + getPageData(1, function (data) {}); + } else { + bs_alert(data.data); + } + } + + }) + }); + + $("#announcement-form") + .formValidation({ + framework: "bootstrap", + fields: { + title: { + validators: { + notEmpty: { + message: "请填写公告标题" + } + } + } + } + } + ).on('success.form.fv', function (e) { + e.preventDefault(); + var title = $("#title").val(); + var content = editor1.getValue(); + if (content == "") { + bs_alert("请填写公告内容") + return; + } + $.ajax({ + beforeSend: csrfHeader, + url: "/api/admin/announcement/", + data: {title: title, content: content}, + dataType: "json", + method: "post", + success: function (data) { + if (!data.code) { + bs_alert("提交成功!"); + $("#title").val(""); + editor1.setValue(""); + getPageData(1, function (data) {}); + } else { + bs_alert(data.data); + } + } + + }) + }); + +}); \ No newline at end of file From a66b755aa3327acd024db892e2e616be707646f1 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Fri, 7 Aug 2015 17:11:20 +0800 Subject: [PATCH 037/304] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20footer=20=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E4=BD=8D=E7=BD=AE=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/css/global.css | 16 ++++++++++++++-- static/src/css/oj.css | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/static/src/css/global.css b/static/src/css/global.css index edc04a7..c55a0bb 100644 --- a/static/src/css/global.css +++ b/static/src/css/global.css @@ -1,3 +1,16 @@ +html{ + height: 100%; +} + +body{ + height:100%; /*使内容高度和body一样*/ + margin-bottom:-80px;/*向上缩减80像素,不至于footer超出屏幕可视范围*/ +} + +.main{ + padding-bottom: 120px; +} + img { max-width: 100%; height: auto; @@ -6,8 +19,7 @@ img { .footer { left: 0; right: 0; - position: absolute; - bottom: 30px; + height: 80px } label { diff --git a/static/src/css/oj.css b/static/src/css/oj.css index 5e1bfc5..cc6ac62 100644 --- a/static/src/css/oj.css +++ b/static/src/css/oj.css @@ -1,3 +1,4 @@ +@import url("global.css"); @import url("bootstrap/bootstrap.min.css"); @import url("bootstrap/todc-bootstrap.min.css"); @import url("codeMirror/codemirror.css"); From 2475407597b9f9078a319f286c9c64beacb55914 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Fri, 7 Aug 2015 17:12:08 +0800 Subject: [PATCH 038/304] =?UTF-8?q?=E5=89=8D=E5=8F=B0=E7=BD=91=E9=A1=B5?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=B0=E7=9A=84=20class=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20footer=20=E6=98=BE=E7=A4=BA=E4=BD=8D=E7=BD=AE?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- template/oj/account/change_password.html | 2 +- template/oj/account/login.html | 2 +- template/oj/account/register.html | 2 +- template/oj/contest/problems.html | 2 +- template/oj/problem/problem.html | 2 +- template/oj/problem/problem_list.html | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/template/oj/account/change_password.html b/template/oj/account/change_password.html index 65de447..dd0d84e 100644 --- a/template/oj/account/change_password.html +++ b/template/oj/account/change_password.html @@ -1,6 +1,6 @@ {% extends "oj_base.html" %} {% block body %} -
+

修改密码

diff --git a/template/oj/account/login.html b/template/oj/account/login.html index e9ddbf2..fb4249b 100644 --- a/template/oj/account/login.html +++ b/template/oj/account/login.html @@ -1,6 +1,6 @@ {% extends "oj_base.html" %} {% block body %} -
+

用户登录

diff --git a/template/oj/account/register.html b/template/oj/account/register.html index 34dc9a4..02d66c8 100644 --- a/template/oj/account/register.html +++ b/template/oj/account/register.html @@ -1,6 +1,6 @@ {% extends "oj_base.html" %} {% block body %} -
+

用户注册

diff --git a/template/oj/contest/problems.html b/template/oj/contest/problems.html index 88dd9d2..450415f 100644 --- a/template/oj/contest/problems.html +++ b/template/oj/contest/problems.html @@ -1,6 +1,6 @@ {% extends "oj_base.html" %} {% block body %} -
+
- +
+ 请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:
+ 1.in 1.out 2.in 2.out +
+ + + + + + + + + + + +
编号输入文件名输出文件名
{{$index}}{{el.input}}{{el.output}}
@@ -184,4 +214,5 @@
- \ No newline at end of file + + \ No newline at end of file diff --git a/template/admin/problem/add_problem.html b/template/admin/problem/add_problem.html index 0c6dad0..6ebb49a 100644 --- a/template/admin/problem/add_problem.html +++ b/template/admin/problem/add_problem.html @@ -12,7 +12,7 @@
- +
From 70a77cccc87617713c5081a563c87e59cc21f16c Mon Sep 17 00:00:00 2001 From: esp Date: Mon, 10 Aug 2015 17:00:39 +0800 Subject: [PATCH 095/304] =?UTF-8?q?[=E5=89=8D=E7=AB=AF]=E4=BF=AE=E6=AD=A3c?= =?UTF-8?q?srf.js=20=20=20=20uploader.js=20=20=E7=9A=84=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=20[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/utils/csrf.js | 2 +- static/src/js/utils/uploader.js | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/static/src/js/utils/csrf.js b/static/src/js/utils/csrf.js index 6a004b4..0e21e0f 100644 --- a/static/src/js/utils/csrf.js +++ b/static/src/js/utils/csrf.js @@ -11,7 +11,7 @@ define("csrf",function(){ } function csrfHeader(){ // jquery的请求 - if(arguments.length == 1) { + if(arguments.length == 2) { arguments[0].setRequestHeader("X-CSRFToken", get_cookie("csrftoken")); } // 百度webuploader 的请求 diff --git a/static/src/js/utils/uploader.js b/static/src/js/utils/uploader.js index ec418c5..83c2b1f 100644 --- a/static/src/js/utils/uploader.js +++ b/static/src/js/utils/uploader.js @@ -1,20 +1,23 @@ -define("uploader", ["webuploader"], function(webuploader){ - function uploader(selector, server) { - return webuploader.create({ - +define("uploader", ["webuploader", "csrf"], function(webuploader,csrf){ + function uploader(selector, server, onSuccess) { + var Webuploader= webuploader.create({ + auto: true, // swf文件路径 - swf: "/js/Uploader.swf", - + swf: "/static/img/Uploader.swf", // 文件接收服务端。 server: server, - // 选择文件的按钮。可选。 // 内部根据当前运行是创建,可能是input元素,也可能是flash. pick: selector, - // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传! - resize: false + resize: false, + uploadBeforeSend : csrf }); + Webuploader.on("uploadBeforeSend",csrf); + Webuploader.on("uploadSuccess", onSuccess); + + return Webuploader; } + return uploader; }); \ No newline at end of file From a6268dcb53b62cd7af1e787356855e2f7ff20a82 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 10 Aug 2015 17:59:39 +0800 Subject: [PATCH 096/304] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E9=A2=98=E7=9B=AE?= =?UTF-8?q?=E6=A0=87=E7=AD=BE=E7=9B=B8=E5=85=B3=E7=9A=84=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 3 +- .../0002_remove_problemtag_description.py | 18 ++++++++++++ problem/models.py | 2 +- problem/serizalizers.py | 11 +++++++- problem/views.py | 28 ++++++++++++++++++- 5 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 problem/migrations/0002_remove_problemtag_description.py diff --git a/oj/urls.py b/oj/urls.py index 1431dc6..fbe7856 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -11,7 +11,7 @@ from group.views import GroupAdminAPIView from admin.views import AdminTemplateView from problem.views import ProblemAdminAPIView -from problem.views import TestCaseUploadAPIView +from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView urlpatterns = [ @@ -41,4 +41,5 @@ urlpatterns = [ url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"), url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="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"), ] diff --git a/problem/migrations/0002_remove_problemtag_description.py b/problem/migrations/0002_remove_problemtag_description.py new file mode 100644 index 0000000..2953e68 --- /dev/null +++ b/problem/migrations/0002_remove_problemtag_description.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('problem', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='problemtag', + name='description', + ), + ] diff --git a/problem/models.py b/problem/models.py index 0132072..3e236af 100644 --- a/problem/models.py +++ b/problem/models.py @@ -6,7 +6,7 @@ from account.models import User class ProblemTag(models.Model): name = models.CharField(max_length=30) - description = models.CharField(max_length=50) + # description = models.CharField(max_length=50) class Meta: db_table = "problem_tag" diff --git a/problem/serizalizers.py b/problem/serizalizers.py index 441c9c5..66f3d75 100644 --- a/problem/serizalizers.py +++ b/problem/serizalizers.py @@ -4,7 +4,7 @@ import json from rest_framework import serializers from account.models import User -from .models import Problem +from .models import Problem, ProblemTag class ProblemSampleSerializer(serializers.ListField): @@ -60,3 +60,12 @@ class EditProblemSerializer(serializers.Serializer): hint = serializers.CharField(max_length=10000) visible = serializers.BooleanField() + + +class ProblemTagSerializer(serializers.ModelSerializer): + class Meta: + model = ProblemTag + + +class CreateProblemTagSerializer(serializers.Serializer): + name = serializers.CharField(max_length=10) \ No newline at end of file diff --git a/problem/views.py b/problem/views.py index 0a5490f..eda2c72 100644 --- a/problem/views.py +++ b/problem/views.py @@ -11,10 +11,36 @@ 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 -from .serizalizers import CreateProblemSerializer, EditProblemSerializer, ProblemSerializer +from .serizalizers import (CreateProblemSerializer, EditProblemSerializer, ProblemSerializer, + ProblemTagSerializer, CreateProblemTagSerializer) from .models import Problem, ProblemTag +class ProblemTagAdminAPIView(APIView): + def post(self, request): + """ + 创建标签的接口 + --- + request_serializer: CreateProblemTagSerializer + """ + serializer = CreateProblemTagSerializer(data=request.data) + if serializer.is_valid(): + try: + tag = ProblemTag.objects.get(name=serializer.data["name"]) + except ProblemTag.DoesNotExist: + tag = ProblemTag.objects.create(name=serializer.data["name"]) + return success_response(ProblemTagSerializer(tag).data) + else: + return error_response(serializer) + + def get(self, request): + keyword = request.GET.get("keyword", None) + if not keyword: + return error_response(u"参数错误") + tags = ProblemTag.objects.filter(name__contains=keyword) + return success_response(ProblemTagSerializer(tags, many=True).data) + + def problem_page(request, problem_id): From 82da537a50436d51431d3dea5a8b860182253005 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Mon, 10 Aug 2015 18:04:35 +0800 Subject: [PATCH 097/304] =?UTF-8?q?=E5=A2=9E=E6=B7=BBannouncementPage?= =?UTF-8?q?=E9=83=A8=E5=88=86=E7=9A=84=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- announcement/tests.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/announcement/tests.py b/announcement/tests.py index 435da34..708f3c6 100644 --- a/announcement/tests.py +++ b/announcement/tests.py @@ -1,5 +1,6 @@ # coding=utf-8 from django.core.urlresolvers import reverse +from django.test import TestCase from rest_framework.test import APITestCase, APIClient @@ -87,3 +88,27 @@ class AnnouncementAPITest(APITestCase): self.assertEqual(response.data["code"], 0) for item in response.data["data"]: self.assertEqual(item["visible"], True) + + +class AnnouncementPageTest(TestCase): + def setUp(self): + user = User.objects.create(username="test") + user.set_password("testaa") + user.save() + Announcement.objects.create(title="aa", + content="AA", + created_by=User.objects.get(username="test"), + visible=True) + + Announcement.objects.create(title="bb", + content="BB", + created_by=User.objects.get(username="test"), + visible=False) + + def test_success_announcement(self): + response = self.client.get('/announcement/1/') + self.assertEqual(response.status_code, 200) + + def test_announcement_does_not_exist(self): + response = self.client.get('/announcement/3/') + self.assertTemplateUsed(response, "utils/error.html") From 5254f428b4aac9f07660b78c8189f4c99121d23a Mon Sep 17 00:00:00 2001 From: esp Date: Mon, 10 Aug 2015 18:05:22 +0800 Subject: [PATCH 098/304] =?UTF-8?q?[=E5=89=8D=E7=AB=AF-=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=97=AE=E9=A2=98]=E6=B7=BB=E5=8A=A0=E6=9D=A5=E6=BA=90?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=20=20=20[CI=20SKIP]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/admin/problem/add_problem.js | 1 + template/admin/problem/add_problem.html | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/static/src/js/app/admin/problem/add_problem.js b/static/src/js/app/admin/problem/add_problem.js index c0ac63f..09862d3 100644 --- a/static/src/js/app/admin/problem/add_problem.js +++ b/static/src/js/app/admin/problem/add_problem.js @@ -98,6 +98,7 @@ require(["jquery", "avalon", "editor", "uploader", "bs_alert", "tagEditor", "val test_case_id: "", testCaseList: [], uploadSuccess: false, + source: "", checkTag: function () { alert("11"); if (event.keyCode == 13) diff --git a/template/admin/problem/add_problem.html b/template/admin/problem/add_problem.html index 6ebb49a..1c78a9e 100644 --- a/template/admin/problem/add_problem.html +++ b/template/admin/problem/add_problem.html @@ -1,10 +1,14 @@
-
+
+
+ + +
From 402c7ee87fcb97c25f14355b91fedf8e3ca138e3 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Mon, 10 Aug 2015 18:07:08 +0800 Subject: [PATCH 099/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E3=80=81=E4=BF=AE=E6=94=B9=E5=B0=8F=E7=BB=84=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E5=B0=8F=E7=BB=84=E5=88=97=E8=A1=A8=E6=88=96?= =?UTF-8?q?=E8=80=85=E6=98=AF=E5=8D=95=E4=B8=AA=E5=B0=8F=E7=BB=84=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group/tests.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/group/tests.py b/group/tests.py index 7ce503c..522ac42 100644 --- a/group/tests.py +++ b/group/tests.py @@ -1,3 +1,85 @@ +# coding=utf-8 from django.test import TestCase -# Create your tests here. +from django.core.urlresolvers import reverse + +from rest_framework.test import APITestCase, APIClient + +from account.models import User, SUPER_ADMIN +from group.models import Group + + +class GroupAdminAPITest(APITestCase): + # 以下是创建小组的测试 + def setUp(self): + self.client = APIClient() + self.url = reverse('group_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 = {"name": "group1"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data["code"], 1) + + def test_success_group(self): + self.client.login(username="test", password="testaa") + data = {"name": "group1", "description": "des1", "join_group_setting": "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 = {"name": "group1"} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 1) + + def test_edit_group_does_not_exist(self): + self.client.login(username="test", password="testaa") + group = Group.objects.create(name="group1", description="des1", + join_group_setting="1", visible="True", + admin=User.objects.get(username="test")) + data = {"group_id": group.id + 1, "name": "group0", "description": "des0", + "join_group_setting": 2} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"小组不存在"}) + + def test_success_edit_group(self): + self.client.login(username="test", password="testaa") + group = Group.objects.create(name="group1", description="des1", + join_group_setting="1", visible="True", + admin=User.objects.get(username="test")) + data = {"group_id": group.id, "name": "group0", "description": "des0", + "join_group_setting": 2} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 0) + + # 以下是查询小组列表或者是单个小组时的测试 + def test_select_group_does_not_exist(self): + self.client.login(username="test", password="testaa") + group = Group.objects.create(name="group1", description="des1", + join_group_setting="1", visible="True", + admin=User.objects.get(username="test")) + data = {"group_id": group.id + 1, "name": "group0", "description": "des0", + "join_group_setting": 2} + response = self.client.get(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"小组不存在"}) + + def test_success_select_group(self): + self.client.login(username="test", password="testaa") + group = Group.objects.create(name="group1", description="des1", + join_group_setting="1", visible="True", + admin=User.objects.get(username="test")) + data = {"group_id": group.id, "name": "group0", "description": "des0", + "join_group_setting": 2} + response = self.client.get(self.url, data=data) + self.assertEqual(response.data["code"], 0) + + def test_success_get_data(self): + self.client.login(username="test", password="testaa") + self.assertEqual(self.client.get(self.url).data["code"], 0) + + From 441b7383bcf2983a793f659863f2db82087bccd3 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 10 Aug 2015 20:18:46 +0800 Subject: [PATCH 100/304] =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=20js=20subm?= =?UTF-8?q?it=5Fproblem=20->=20problem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/js/app/oj/problem/{submit_code.js => problem.js} | 8 +++++--- static/src/js/config.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) rename static/src/js/app/oj/problem/{submit_code.js => problem.js} (84%) diff --git a/static/src/js/app/oj/problem/submit_code.js b/static/src/js/app/oj/problem/problem.js similarity index 84% rename from static/src/js/app/oj/problem/submit_code.js rename to static/src/js/app/oj/problem/problem.js index 287554c..a713c07 100644 --- a/static/src/js/app/oj/problem/submit_code.js +++ b/static/src/js/app/oj/problem/problem.js @@ -22,10 +22,12 @@ require(["jquery", "code_mirror"], function ($, code_mirror) { setTimeout( function () { $("#a").animate({opacity: '1'}) - } - , - 3); + }, 3); + }); + $("#show-more-btn").click(function(){ + $(".hide").attr("class", "problem-section"); + $("#show-more-btn").hide(); }) }); diff --git a/static/src/js/config.js b/static/src/js/config.js index f852cc8..0ecf314 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -12,7 +12,7 @@ var require = { validation: "utils/validation", code_mirror: "utils/code_mirror", bs_alert: "utils/bs_alert", - submit_code: "app/oj/problem/submit_code", + problem: "app/oj/problem/problem", contest: "app/admin/contest/contest", csrf: "utils/csrf", admin: "app/admin/admin", From 502b4944f1083368a854732b213e82eeaa0323db Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 10 Aug 2015 20:19:22 +0800 Subject: [PATCH 101/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A2=98=E7=9B=AE?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=92=8C=E6=88=91=E7=9A=84=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 2 + problem/views.py | 16 +- template/oj/problem/my_solution.html | 31 ++++ template/oj/problem/my_solutions_list.html | 53 +++++++ template/oj/problem/problem.html | 165 ++++++++++++--------- 5 files changed, 199 insertions(+), 68 deletions(-) create mode 100644 template/oj/problem/my_solution.html create mode 100644 template/oj/problem/my_solutions_list.html diff --git a/oj/urls.py b/oj/urls.py index fbe7856..27635c7 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -42,4 +42,6 @@ urlpatterns = [ url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="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_solutions/', "problem.views.problem_my_solutions_list_page", name="problem_my_solutions_page"), + url(r'^my_solution/(?P\d+)/$', "problem.views.my_solution", name="my_solution_page"), ] diff --git a/problem/views.py b/problem/views.py index eda2c72..8cdf328 100644 --- a/problem/views.py +++ b/problem/views.py @@ -44,8 +44,20 @@ class ProblemTagAdminAPIView(APIView): def problem_page(request, problem_id): - # todo - return render(request, "oj/problem/problem.html") + # try: + # problem = Problem.objects.get(id=problem_id) + # except Problem.DoesNotExist: + # return render(request, "utils/error.html", {"error": u"题目不存在"}) + return render(request, "oj/problem/problem.html", {"problem": {}}) + + +def problem_my_solutions_list_page(request, problem_id): + return render(request, "oj/problem/my_solutions_list.html") + + +def my_solution(request, solution_id): + return render(request, "oj/problem/my_solution.html") + class ProblemAdminAPIView(APIView): diff --git a/template/oj/problem/my_solution.html b/template/oj/problem/my_solution.html new file mode 100644 index 0000000..8f05b10 --- /dev/null +++ b/template/oj/problem/my_solution.html @@ -0,0 +1,31 @@ +{% extends 'oj_base.html' %} + +{% block body %} + +
+ +

Battle Over Cities - Hard Version

+

cpu: 1000ms 内存: 256M

+
+
+

运行结果:Accepted

+

cpu: 1000ms 内存: 256M 语言:python

+
+
+
+ +
+
+ +{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/my_solutions_list.html b/template/oj/problem/my_solutions_list.html new file mode 100644 index 0000000..613410a --- /dev/null +++ b/template/oj/problem/my_solutions_list.html @@ -0,0 +1,53 @@ +{% extends 'oj_base.html' %} + +{% block body %} + +
+ +

Battle Over Cities - Hard Version

+

cpu: 1000ms 内存: 256M

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#提交时间结果运行时间运行内存语言
11Error Format333
2WrongWrong Answer@fat33
3LarryAccepted@twitter33
+
+{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/problem.html b/template/oj/problem/problem.html index cbdc0e5..a4dde86 100644 --- a/template/oj/problem/problem.html +++ b/template/oj/problem/problem.html @@ -1,93 +1,126 @@ {% extends 'oj_base.html' %} {% block body %} -
- -

Battle Over Cities - Hard Version

+
+ +

Battle Over Cities - Hard Version

-

cpu: 1000ms 内存: 256M

+

发布时间: 2015-07-05 CPU: 1000ms 内存: 256M

-
-
- +
+
+ -

n的阶乘定义为n!=1*2*3*……*n 如3!=6 - n!通常最后会有很多0,如5!=120 最后有一个0,现在统计n!去除末尾的0后,最后k位是多少

-
-
- +

n的阶乘定义为n!=1*2*3*……*n 如3!=6 + n!通常最后会有很多0,如5!=120 最后有一个0,现在统计n!去除末尾的0后,最后k位是多少

+
+
+ -

第一行包括两个数n,k

-
-
- +

第一行包括两个数n,k

+
+
+ -

第一行包括两个数n,k

-
-
- +

第一行包括两个数n,k

+
+
+
 1 2 3 4 5 6 7
 1 2 3 4 5 6 7
-
-
+
+
- +
 1 2 3 4 5 6 7
-
-
- -
- - -
-
-
- - -
-
-
- - +
+ +
+1 2 3 4 5 6 7
+1 2 3 4 5 6 7
-
-
-
- diff --git a/template/admin/user/user_list.html b/template/admin/user/user_list.html index 8f87af1..14d020d 100644 --- a/template/admin/user/user_list.html +++ b/template/admin/user/user_list.html @@ -39,8 +39,8 @@
页数:{{page}}/{{page_count}}   - 上一页 - 下一页 + +

修改用户信息

From d93c4559a597ec39d1fcfbb13eeb17e83ef9bd7b Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 11 Aug 2015 20:12:05 +0800 Subject: [PATCH 121/304] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E5=B0=8F=E7=BB=84=E4=BF=A1=E6=81=AF=E7=9A=84=E6=97=B6=E5=80=99?= =?UTF-8?q?=E7=9A=84=20radio=20=E4=B8=8D=E8=83=BD=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=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/js/app/admin/group/group_detail.js | 10 +++------- template/admin/group/group_detail.html | 6 +++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/static/src/js/app/admin/group/group_detail.js b/static/src/js/app/admin/group/group_detail.js index 93a5d43..0c6561e 100644 --- a/static/src/js/app/admin/group/group_detail.js +++ b/static/src/js/app/admin/group/group_detail.js @@ -14,11 +14,7 @@ require(["jquery", "avalon", "csrf", "bs_alert", "validation"], function ($, ava name: "", description: "", join_group_setting: {0: false, 1: false, 2: false}, - checked_setting: 0, - - getSettingChecked: function (setting) { - return setting == vm.join_group_setting; - }, + checked_setting: "0", updateGroupInfo: function () { $.ajax({ @@ -26,7 +22,7 @@ require(["jquery", "avalon", "csrf", "bs_alert", "validation"], function ($, ava url: "/api/admin/group/", method: "put", data: {group_id: avalon.vmodels.admin.group_id, name: vm.name, - description: vm.description, join_group_setting: vm.join_group_setting}, + description: vm.description, join_group_setting: vm.checked_setting}, dataType: "json", success: function (data) { if (!data.code) { @@ -107,7 +103,7 @@ require(["jquery", "avalon", "csrf", "bs_alert", "validation"], function ($, ava if (!data.code) { vm.name = data.data.name; vm.description = data.data.description; - vm.join_group_setting[data.data.join_group_setting] = true; + vm.checked_setting = data.data.join_group_setting.toString(); } else { bs_alert(data.data); diff --git a/template/admin/group/group_detail.html b/template/admin/group/group_detail.html index e087838..dc695e0 100644 --- a/template/admin/group/group_detail.html +++ b/template/admin/group/group_detail.html @@ -43,9 +43,9 @@
- 允许任何人加入 - 提交请求后管理员审核 - 不允许任何人加入 + 允许任何人加入 + 提交请求后管理员审核 + 不允许任何人加入
From b9f4cbacf6856c5a3bcdfeed3c74ec3cf442c75f Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 11 Aug 2015 20:23:22 +0800 Subject: [PATCH 122/304] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20dockerfile?= =?UTF-8?q?=EF=BC=8C=E8=87=AA=E5=8A=A8=E5=88=9B=E5=BB=BA=E4=B8=B4=E6=97=B6?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 6dbbdd6..f10b84c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,5 +7,7 @@ WORKDIR /var/oj/ RUN pip install -r requirements.txt EXPOSE 8080 RUN mkdir LOG +RUN mkdir test_case +RUN mkdir tmp RUN python manage.py migrate CMD python manage.py runserver 0.0.0.0:8080 From 408776910dc720184e3f1e101e7ea998c186a489 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 12 Aug 2015 10:24:06 +0800 Subject: [PATCH 123/304] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E4=B8=80=E4=B8=8B?= =?UTF-8?q?=E5=88=A4=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- judge/controller/tasks.py | 5 +- judge/judger/client.py | 77 +---------------------- judge/judger/run.py | 125 ++++++++++++++++++++++++++++++++++++++ requirements.txt | 3 +- 4 files changed, 132 insertions(+), 78 deletions(-) create mode 100644 judge/judger/run.py diff --git a/judge/controller/tasks.py b/judge/controller/tasks.py index f0c32bc..d87cde9 100644 --- a/judge/controller/tasks.py +++ b/judge/controller/tasks.py @@ -1,8 +1,11 @@ # coding=utf-8 from __future__ import absolute_import from judge.controller.celery import app +import subprocess32 as subprocess + +subprocess.call("ping baidu.com", timeout=5) @app.task def judge(source_code, language, test_case_id): - print source_code, language, test_case_id \ No newline at end of file + pass \ No newline at end of file diff --git a/judge/judger/client.py b/judge/judger/client.py index cdac0b2..d159583 100644 --- a/judge/judger/client.py +++ b/judge/judger/client.py @@ -170,79 +170,4 @@ class JudgeClient(object): # http://stackoverflow.com/questions/25382455/python-notimplementederror-pool-objects-cannot-be-passed-between-processes self_dict = self.__dict__.copy() del self_dict['_pool'] - return self_dict - - - -c_src = r""" -#include -#include -int main() -{ - FILE *fp; - fp = NULL; - fprintf(fp, "This is testing for fprintf...\n"); - fputs("This is testing for fputs...\n", fp); - fclose(fp); - printf("111111"); - return 0; -} -""" - -cpp_src = r""" -#include - -using namespace std; - -int main() -{ - int a,b; - cin >> a >> b; - cout << a+b; - return 0; -} -""" - -java_src = r""" -import java.io.*; -import java.util.*; -11 -public class Main -{ - public static void main(String[] args) - { - Scanner in = new Scanner(System.in); - PrintWriter out = new PrintWriter(System.out); - - int a = in.nextInt(); - int b = in.nextInt(); - out.print(a + b); - throw new EmptyStackException(); - - } -} -""" -def judge(languege_code, source_string): - language = languages[str(languege_code)] - src_path = judger_workspace + language["src_name"] - f = open(src_path, "w") - f.write(source_string) - f.close() - - try: - exe_path = compile_(languages[str(languege_code)], src_path, judger_workspace) - except Exception as e: - print e - return [{"result": result["compile_error"]}] - - client = JudgeClient(language_code=languege_code, - exe_path=exe_path, - max_cpu_time=1000000, - max_real_time=200000, - max_memory=1000, - test_case_dir="/var/test_cases/1/") - return client.run() - -print judge(1, c_src) -print judge(2, cpp_src) -print judge(3, java_src) \ No newline at end of file + return self_dict \ No newline at end of file diff --git a/judge/judger/run.py b/judge/judger/run.py new file mode 100644 index 0000000..6a67e16 --- /dev/null +++ b/judge/judger/run.py @@ -0,0 +1,125 @@ +# coding=utf-8 +import sys +from client import JudgeClient +from language import languages +from compiler import compile_ +from result import result + +# +# c_src = r""" +# #include +# #include +# int main() +# { +# FILE *fp; +# fp = NULL; +# fprintf(fp, "This is testing for fprintf...\n"); +# fputs("This is testing for fputs...\n", fp); +# fclose(fp); +# printf("111111"); +# return 0; +# } +# """ +# +# cpp_src = r""" +# #include +# +# using namespace std; +# +# int main() +# { +# int a,b; +# cin >> a >> b; +# cout << a+b; +# return 0; +# } +# """ +# +# java_src = r""" +# import java.io.*; +# import java.util.*; +# 11 +# public class Main +# { +# public static void main(String[] args) +# { +# Scanner in = new Scanner(System.in); +# PrintWriter out = new PrintWriter(System.out); +# +# int a = in.nextInt(); +# int b = in.nextInt(); +# out.print(a + b); +# throw new EmptyStackException(); +# +# } +# } +# """ +# def judge(language_code, source_string): +# language = languages[str(language_code)] +# src_path = judger_workspace + language["src_name"] +# f = open(src_path, "w") +# f.write(source_string) +# f.close() +# +# try: +# exe_path = compile_(languages[str(language_code)], src_path, judger_workspace) +# except Exception as e: +# print e +# return [{"result": result["compile_error"]}] +# +# client = JudgeClient(language_code=language_code, +# exe_path=exe_path, +# max_cpu_time=1000000, +# max_real_time=200000, +# max_memory=1000, +# test_case_dir="/var/test_cases/1/") +# return client.run() +# +# print judge(1, c_src) +# print judge(2, cpp_src) +# print judge(3, java_src) + +judger_workspace = "/var/judger/" +# 简单的解析命令行参数 +# 参数有 -solution_id -max_cpu_time -max_memory -test_case_id +# 获取到的值是['xxx.py', '-solution_id', '1111', '-max_cpu_time', '1000', '-max_memory', '100', '-test_case_id', 'aaaa'] +args = sys.argv +solution_id = args[2] +max_cpu_time = args[4] +max_memory = args[6] +test_case_id = args[8] + +# todo 去数据库查一下 +language_code = 1 +source_string = """ +#include +int main() +{ + int a, b; + scanf("%d %d", &a, &b); + printf("%d", a + b); + return 0; +} +""" +language = languages[str(language_code)] +src_path = judger_workspace + language["src_name"] +f = open(src_path, "w") +f.write(source_string) +f.close() + +try: + exe_path = compile_(languages[str(language_code)], src_path, judger_workspace) +except Exception as e: + print e + print [{"result": result["compile_error"]}] + exit() + +client = JudgeClient(language_code=language_code, + exe_path=exe_path, + max_cpu_time=1000000, + max_real_time=200000, + max_memory=1000, + test_case_dir="/var/test_cases/1/") +print client.run() + + diff --git a/requirements.txt b/requirements.txt index bb0fce3..9835709 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,5 @@ djangorestframework django-rest-swagger celery gunicorn -coverage \ No newline at end of file +coverage +subprocess32 \ No newline at end of file From 8979def927914143f150ebd0d3c7586ac002698a Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 12 Aug 2015 10:56:03 +0800 Subject: [PATCH 124/304] =?UTF-8?q?=E7=BC=96=E8=AF=91=E6=97=B6=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- judge/judger/language.py | 4 +-- judge/judger/run.py | 75 +--------------------------------------- 2 files changed, 3 insertions(+), 76 deletions(-) diff --git a/judge/judger/language.py b/judge/judger/language.py index 5923edb..3cbf002 100644 --- a/judge/judger/language.py +++ b/judge/judger/language.py @@ -6,14 +6,14 @@ languages = { "name": "c", "src_name": "main.c", "code": 1, - "compile_command": "gcc -DONLINE_JUDGE -O2 -Wall -std=c99 -pipe {src_path} -lm -o {exe_path}main", + "compile_command": "gcc -DONLINE_JUDGE -O2 -w -std=c99 -pipe {src_path} -lm -o {exe_path}main", "execute_command": "{exe_path}main" }, "2": { "name": "cpp", "src_name": "main.cpp", "code": 2, - "compile_command": "g++ -DONLINE_JUDGE -O2 -Wall -std=c++11 -pipe {src_path} -lm -o {exe_path}main", + "compile_command": "g++ -DONLINE_JUDGE -O2 -w -std=c++11 -pipe {src_path} -lm -o {exe_path}main", "execute_command": "{exe_path}main" }, "3": { diff --git a/judge/judger/run.py b/judge/judger/run.py index 6a67e16..03474cf 100644 --- a/judge/judger/run.py +++ b/judge/judger/run.py @@ -5,81 +5,8 @@ from language import languages from compiler import compile_ from result import result -# -# c_src = r""" -# #include -# #include -# int main() -# { -# FILE *fp; -# fp = NULL; -# fprintf(fp, "This is testing for fprintf...\n"); -# fputs("This is testing for fputs...\n", fp); -# fclose(fp); -# printf("111111"); -# return 0; -# } -# """ -# -# cpp_src = r""" -# #include -# -# using namespace std; -# -# int main() -# { -# int a,b; -# cin >> a >> b; -# cout << a+b; -# return 0; -# } -# """ -# -# java_src = r""" -# import java.io.*; -# import java.util.*; -# 11 -# public class Main -# { -# public static void main(String[] args) -# { -# Scanner in = new Scanner(System.in); -# PrintWriter out = new PrintWriter(System.out); -# -# int a = in.nextInt(); -# int b = in.nextInt(); -# out.print(a + b); -# throw new EmptyStackException(); -# -# } -# } -# """ -# def judge(language_code, source_string): -# language = languages[str(language_code)] -# src_path = judger_workspace + language["src_name"] -# f = open(src_path, "w") -# f.write(source_string) -# f.close() -# -# try: -# exe_path = compile_(languages[str(language_code)], src_path, judger_workspace) -# except Exception as e: -# print e -# return [{"result": result["compile_error"]}] -# -# client = JudgeClient(language_code=language_code, -# exe_path=exe_path, -# max_cpu_time=1000000, -# max_real_time=200000, -# max_memory=1000, -# test_case_dir="/var/test_cases/1/") -# return client.run() -# -# print judge(1, c_src) -# print judge(2, cpp_src) -# print judge(3, java_src) -judger_workspace = "/var/judger/" +judger_workspace = "/var/judger/run/" # 简单的解析命令行参数 # 参数有 -solution_id -max_cpu_time -max_memory -test_case_id # 获取到的值是['xxx.py', '-solution_id', '1111', '-max_cpu_time', '1000', '-max_memory', '100', '-test_case_id', 'aaaa'] From 88be032a384b3d9b4d095d5802a11fbf29bd1b12 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 12 Aug 2015 14:01:34 +0800 Subject: [PATCH 125/304] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=88=A4=E9=A2=98?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=EF=BC=9B=E5=A2=9E=E5=8A=A0web=20=E5=92=8C=20?= =?UTF-8?q?mongodb=20=E6=95=B0=E6=8D=AE=E5=BA=93=E7=9A=84=E6=93=8D?= =?UTF-8?q?=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- solution/models.py => config/__init__.py | 0 config/config.py | 2 + judge/README.md | 3 ++ judge/judger/language.py | 6 +-- judge/judger/run.py | 43 ++++++++------- oj/local_settings.py | 23 +++++--- oj/urls.py | 2 + problem/views.py | 13 ++--- requirements.txt | 3 +- solution/views.py | 3 -- static/src/js/app/oj/problem/problem.js | 6 ++- {solution => submission}/__init__.py | 0 {solution => submission}/admin.py | 0 .../migrations/__init__.py | 0 submission/models.py | 1 + submission/serializers.py | 10 ++++ {solution => submission}/tests.py | 0 submission/views.py | 52 +++++++++++++++++++ template/oj/problem/problem.html | 6 +-- 19 files changed, 127 insertions(+), 46 deletions(-) rename solution/models.py => config/__init__.py (100%) create mode 100644 config/config.py create mode 100644 judge/README.md delete mode 100644 solution/views.py rename {solution => submission}/__init__.py (100%) rename {solution => submission}/admin.py (100%) rename {solution => submission}/migrations/__init__.py (100%) create mode 100644 submission/models.py create mode 100644 submission/serializers.py rename {solution => submission}/tests.py (100%) create mode 100644 submission/views.py diff --git a/solution/models.py b/config/__init__.py similarity index 100% rename from solution/models.py rename to config/__init__.py diff --git a/config/config.py b/config/config.py new file mode 100644 index 0000000..89d316b --- /dev/null +++ b/config/config.py @@ -0,0 +1,2 @@ +# coding=utf-8 +DB_NAME = "db.sqlite3" diff --git a/judge/README.md b/judge/README.md new file mode 100644 index 0000000..c40444b --- /dev/null +++ b/judge/README.md @@ -0,0 +1,3 @@ +/usr/bin/docker run -t -i --privileged -v /var/test_case/:/var/judger/test_case/ -v /var/code/:/var/judger/code/ judger /bin/bash + +python judge/judger/run.py -solution_id 1 -max_cpu_time 1 -max_memory 1 -test_case_id 1 diff --git a/judge/judger/language.py b/judge/judger/language.py index 3cbf002..c79b296 100644 --- a/judge/judger/language.py +++ b/judge/judger/language.py @@ -2,21 +2,21 @@ languages = { - "1": { + 1: { "name": "c", "src_name": "main.c", "code": 1, "compile_command": "gcc -DONLINE_JUDGE -O2 -w -std=c99 -pipe {src_path} -lm -o {exe_path}main", "execute_command": "{exe_path}main" }, - "2": { + 2: { "name": "cpp", "src_name": "main.cpp", "code": 2, "compile_command": "g++ -DONLINE_JUDGE -O2 -w -std=c++11 -pipe {src_path} -lm -o {exe_path}main", "execute_command": "{exe_path}main" }, - "3": { + 3: { "name": "java", "src_name": "Main.java", "code": 3, diff --git a/judge/judger/run.py b/judge/judger/run.py index 03474cf..f75e81d 100644 --- a/judge/judger/run.py +++ b/judge/judger/run.py @@ -1,12 +1,17 @@ # coding=utf-8 import sys +import pymongo + +from bson.objectid import ObjectId + from client import JudgeClient from language import languages from compiler import compile_ from result import result +from settings import judger_workspace +from oj import settings -judger_workspace = "/var/judger/run/" # 简单的解析命令行参数 # 参数有 -solution_id -max_cpu_time -max_memory -test_case_id # 获取到的值是['xxx.py', '-solution_id', '1111', '-max_cpu_time', '1000', '-max_memory', '100', '-test_case_id', 'aaaa'] @@ -16,37 +21,37 @@ max_cpu_time = args[4] max_memory = args[6] test_case_id = args[8] -# todo 去数据库查一下 -language_code = 1 -source_string = """ -#include -int main() -{ - int a, b; - scanf("%d %d", &a, &b); - printf("%d", a + b); - return 0; -} -""" -language = languages[str(language_code)] -src_path = judger_workspace + language["src_name"] + +mongodb_setting = settings.DATABASES["mongodb"] +connection = pymongo.MongoClient(host=mongodb_setting["HOST"], port=mongodb_setting["PORT"]) +collection = connection["oj"]["oj_submission"] + +submission = collection.find_one({"_id": ObjectId(solution_id)}) +if not submission: + exit() + + +# 将代码写入文件 +language = languages[submission["language"]] +src_path = judger_workspace + "run/" + language["src_name"] f = open(src_path, "w") -f.write(source_string) +f.write(submission["code"]) f.close() +# 编译 try: - exe_path = compile_(languages[str(language_code)], src_path, judger_workspace) + exe_path = compile_(language, src_path, judger_workspace + "run/") except Exception as e: print e print [{"result": result["compile_error"]}] exit() -client = JudgeClient(language_code=language_code, +client = JudgeClient(language_code=language, exe_path=exe_path, max_cpu_time=1000000, max_real_time=200000, max_memory=1000, - test_case_dir="/var/test_cases/1/") + test_case_dir="/var/judger/test_case/" + str(test_case_id) + "/") print client.run() diff --git a/oj/local_settings.py b/oj/local_settings.py index a99e009..2f38496 100644 --- a/oj/local_settings.py +++ b/oj/local_settings.py @@ -1,19 +1,28 @@ # coding=utf-8 import os -LOG_PATH = "LOG/" - -# Database -# https://docs.djangoproject.com/en/1.8/ref/settings/#databases BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# 下面是需要自己修改的 +LOG_PATH = "LOG/" + DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - 'CONN_MAX_AGE': 1, + 'CONN_MAX_AGE': 0.3, + }, + 'mongodb': { + 'HOST': '127.0.0.1', + 'USERNAME': 'root', + 'PASSWORD': 'root', + 'PORT': 27017 } } -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True \ No newline at end of file + +DEBUG = True + +TEST_CASE_DIR = "/var/test_case/" + diff --git a/oj/urls.py b/oj/urls.py index e3fbcda..5c7171f 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -14,6 +14,7 @@ from admin.views import AdminTemplateView from problem.views import ProblemAdminAPIView from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView +from submission.views import SubmissionnAPIView urlpatterns = [ @@ -50,5 +51,6 @@ urlpatterns = [ url(r'^my_solution/(?P\d+)/$', "problem.views.my_solution", name="my_solution_page"), url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), name="join_group_request_admin_api"), + url(r'^api/submission/$', SubmissionnAPIView.as_view(), name="submission_api"), ] diff --git a/problem/views.py b/problem/views.py index f9677b1..e8f3e9a 100644 --- a/problem/views.py +++ b/problem/views.py @@ -10,6 +10,8 @@ from django.db.models import Q from rest_framework.views import APIView +from django.conf import settings + from utils.shortcuts import serializer_invalid_response, error_response, success_response, paginate, rand_str from .serizalizers import (CreateProblemSerializer, EditProblemSerializer, ProblemSerializer, ProblemTagSerializer, CreateProblemTagSerializer) @@ -35,13 +37,6 @@ class ProblemTagAdminAPIView(APIView): def get(self, request): return success_response(ProblemTagSerializer(ProblemTag.objects.all(), many=True).data) - keyword = request.GET.get("keyword", None) - if not keyword: - return error_response(u"参数错误") - tags = ProblemTag.objects.filter(name__contains=keyword) - return success_response(ProblemTagSerializer(tags, many=True).data) - - def problem_page(request, problem_id): @@ -159,7 +154,7 @@ class TestCaseUploadAPIView(APIView): f = request.FILES["file"] - tmp_zip = "tmp/" + rand_str() + ".zip" + tmp_zip = "/tmp/" + rand_str() + ".zip" with open(tmp_zip, "wb") as test_case_zip: for chunk in f: test_case_zip.write(chunk) @@ -192,7 +187,7 @@ class TestCaseUploadAPIView(APIView): return error_response(u"测试用例文件不完整,缺少" + name[0] + ".in") problem_test_dir = rand_str() - test_case_dir = "test_case/" + problem_test_dir + "/" + test_case_dir = settings.TEST_CASE_DIR + "test_case/" + problem_test_dir + "/" # 得到了合法的测试用例文件列表 然后去解压缩 os.mkdir(test_case_dir) diff --git a/requirements.txt b/requirements.txt index 9835709..324d7b0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,5 @@ django-rest-swagger celery gunicorn coverage -subprocess32 \ No newline at end of file +subprocess32 +pymongo \ No newline at end of file diff --git a/solution/views.py b/solution/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/solution/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/static/src/js/app/oj/problem/problem.js b/static/src/js/app/oj/problem/problem.js index a713c07..5b10211 100644 --- a/static/src/js/app/oj/problem/problem.js +++ b/static/src/js/app/oj/problem/problem.js @@ -1,8 +1,9 @@ require(["jquery", "code_mirror"], function ($, code_mirror) { var code_editor = code_mirror($("#code-editor")[0], "text/x-csrc"); + var language = "1"; $("#language-selector").change(function () { - var language = $("#language-selector").val(); + language = $("#language-selector").val(); var language_types = {c: "text/x-csrc", cpp: "text/x-c++src", java: "text/x-java"}; code_editor.setOption("mode", language_types[language]); }); @@ -19,6 +20,9 @@ require(["jquery", "code_mirror"], function ($, code_mirror) { $("#submit-code-button").click(function () { show_loading(); + $.ajax({ + + }); setTimeout( function () { $("#a").animate({opacity: '1'}) diff --git a/solution/__init__.py b/submission/__init__.py similarity index 100% rename from solution/__init__.py rename to submission/__init__.py diff --git a/solution/admin.py b/submission/admin.py similarity index 100% rename from solution/admin.py rename to submission/admin.py diff --git a/solution/migrations/__init__.py b/submission/migrations/__init__.py similarity index 100% rename from solution/migrations/__init__.py rename to submission/migrations/__init__.py diff --git a/submission/models.py b/submission/models.py new file mode 100644 index 0000000..9bad579 --- /dev/null +++ b/submission/models.py @@ -0,0 +1 @@ +# coding=utf-8 diff --git a/submission/serializers.py b/submission/serializers.py new file mode 100644 index 0000000..c64ada6 --- /dev/null +++ b/submission/serializers.py @@ -0,0 +1,10 @@ +# coding=utf-8 +from rest_framework import serializers + + + +class CreateSubmissionSerializer(serializers.Serializer): + problem_id = serializers.IntegerField() + language = serializers.IntegerField() + code = serializers.CharField(max_length=3000) + diff --git a/solution/tests.py b/submission/tests.py similarity index 100% rename from solution/tests.py rename to submission/tests.py diff --git a/submission/views.py b/submission/views.py new file mode 100644 index 0000000..8af26f1 --- /dev/null +++ b/submission/views.py @@ -0,0 +1,52 @@ +# coding=utf-8 +import pymongo +from bson.objectid import ObjectId + +from django.shortcuts import render + +from rest_framework.views import APIView + +from django.conf import settings + +from judge.judger.result import result +from account.decorators import login_required +from utils.shortcuts import serializer_invalid_response, error_response, success_response +from .serializers import CreateSubmissionSerializer + + +class SubmissionnAPIView(APIView): + def _create_mondodb_connection(self): + mongodb_setting = settings.DATABASES["mongodb"] + connection = pymongo.MongoClient(host=mongodb_setting["HOST"], port=mongodb_setting["PORT"]) + return connection["oj"]["oj_submission"] + + # @login_required + def post(self, request): + """ + 提交代码 + --- + request_serializer: CreateSubmissionSerializer + """ + serializer = CreateSubmissionSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + data["user_id"] = request.user.id + data["result"] = result["waiting"] + mongodb_setting = settings.DATABASES["mongodb"] + connection = pymongo.MongoClient(host=mongodb_setting["HOST"], port=mongodb_setting["PORT"]) + collection = connection["oj"]["oj_submission"] + submission_id = str(collection.insert_one(data).inserted_id) + return success_response({"submission_id": submission_id}) + else: + return serializer_invalid_response(serializer) + + # @login_required + def get(self, request): + submission_id = request.GET.get("submission_id", None) + if not submission_id: + return error_response(u"参数错误") + submission = self._create_mondodb_connection().find_one({"_id": ObjectId(submission_id), "user_id": result.user.id}) + if submission: + return success_response({"result": submission["result"]}) + else: + return error_response(u"提交不存在") diff --git a/template/oj/problem/problem.html b/template/oj/problem/problem.html index 5a7a3b6..b3f1623 100644 --- a/template/oj/problem/problem.html +++ b/template/oj/problem/problem.html @@ -66,13 +66,13 @@
From 2ba1fa56691214a9f239da1d34ee11fbcd6225d0 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 12 Aug 2015 14:56:18 +0800 Subject: [PATCH 126/304] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=91=BD=E5=90=8D=E9=A3=8E=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- judge/controller/tasks.py | 10 ++++++---- judge/judger/run.py | 14 +++++++------- problem/views.py | 2 +- submission/views.py | 7 +++++++ 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/judge/controller/tasks.py b/judge/controller/tasks.py index d87cde9..42b88ee 100644 --- a/judge/controller/tasks.py +++ b/judge/controller/tasks.py @@ -3,9 +3,11 @@ from __future__ import absolute_import from judge.controller.celery import app import subprocess32 as subprocess -subprocess.call("ping baidu.com", timeout=5) - @app.task -def judge(source_code, language, test_case_id): - pass \ No newline at end of file +def judge(solution_id, time_limit, memory_limit, test_case_id): + subprocess.call("/usr/bin/docker run -t -i --privileged -v /var/test_case/:/var/judger/test_case/ " + "-v /var/code/:/var/judger/code/ judger python judge/judger/run.py " + "--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % + (solution_id, str(time_limit), str(memory_limit), test_case_id), + timeout=(time_limit / 100) * 20) diff --git a/judge/judger/run.py b/judge/judger/run.py index f75e81d..40da1dc 100644 --- a/judge/judger/run.py +++ b/judge/judger/run.py @@ -13,12 +13,12 @@ from settings import judger_workspace from oj import settings # 简单的解析命令行参数 -# 参数有 -solution_id -max_cpu_time -max_memory -test_case_id -# 获取到的值是['xxx.py', '-solution_id', '1111', '-max_cpu_time', '1000', '-max_memory', '100', '-test_case_id', 'aaaa'] +# 参数有 -solution_id -time_limit -memory_limit -test_case_id +# 获取到的值是['xxx.py', '-solution_id', '1111', '-time_limit', '1000', '-memory_limit', '100', '-test_case_id', 'aaaa'] args = sys.argv solution_id = args[2] -max_cpu_time = args[4] -max_memory = args[6] +time_limit = args[4] +memory_limit = args[6] test_case_id = args[8] @@ -48,9 +48,9 @@ except Exception as e: client = JudgeClient(language_code=language, exe_path=exe_path, - max_cpu_time=1000000, - max_real_time=200000, - max_memory=1000, + max_cpu_time=int(time_limit), + max_real_time=int(time_limit) * 2, + max_memory=int(memory_limit), test_case_dir="/var/judger/test_case/" + str(test_case_id) + "/") print client.run() diff --git a/problem/views.py b/problem/views.py index e8f3e9a..7669816 100644 --- a/problem/views.py +++ b/problem/views.py @@ -187,7 +187,7 @@ class TestCaseUploadAPIView(APIView): return error_response(u"测试用例文件不完整,缺少" + name[0] + ".in") problem_test_dir = rand_str() - test_case_dir = settings.TEST_CASE_DIR + "test_case/" + problem_test_dir + "/" + test_case_dir = settings.TEST_CASE_DIR + problem_test_dir + "/" # 得到了合法的测试用例文件列表 然后去解压缩 os.mkdir(test_case_dir) diff --git a/submission/views.py b/submission/views.py index 8af26f1..5425680 100644 --- a/submission/views.py +++ b/submission/views.py @@ -9,7 +9,9 @@ from rest_framework.views import APIView from django.conf import settings from judge.judger.result import result +from judge.controller.tasks import judge from account.decorators import login_required +from problem.models import Problem from utils.shortcuts import serializer_invalid_response, error_response, success_response from .serializers import CreateSubmissionSerializer @@ -32,10 +34,15 @@ class SubmissionnAPIView(APIView): data = serializer.data data["user_id"] = request.user.id data["result"] = result["waiting"] + try: + problem = Problem.objects.get(id=data["problem_id"]) + except Problem.DoesNotExist: + return error_response(u"题目不存在") mongodb_setting = settings.DATABASES["mongodb"] connection = pymongo.MongoClient(host=mongodb_setting["HOST"], port=mongodb_setting["PORT"]) collection = connection["oj"]["oj_submission"] submission_id = str(collection.insert_one(data).inserted_id) + judge.deply(submission_id, problem.max_cpu_time, problem_) return success_response({"submission_id": submission_id}) else: return serializer_invalid_response(serializer) From 702d51f495032233df3b7ad29a2cb59631ce1cae Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 12 Aug 2015 15:06:19 +0800 Subject: [PATCH 127/304] fix typo --- submission/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submission/views.py b/submission/views.py index 5425680..1208632 100644 --- a/submission/views.py +++ b/submission/views.py @@ -42,7 +42,7 @@ class SubmissionnAPIView(APIView): connection = pymongo.MongoClient(host=mongodb_setting["HOST"], port=mongodb_setting["PORT"]) collection = connection["oj"]["oj_submission"] submission_id = str(collection.insert_one(data).inserted_id) - judge.deply(submission_id, problem.max_cpu_time, problem_) + judge.delay(submission_id, problem.max_cpu_time, problem.max_memory, problem.test_case_id) return success_response({"submission_id": submission_id}) else: return serializer_invalid_response(serializer) From b8d3c1ce2de6c466fe22024d755dda43f1d8c08f Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Wed, 12 Aug 2015 15:16:19 +0800 Subject: [PATCH 128/304] =?UTF-8?q?group=E6=B5=8B=E8=AF=95=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20=E5=8A=A0=E5=85=A5=E5=B0=8F=E7=BB=84=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E6=B5=8B=E8=AF=95=E5=B9=B6=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group/tests.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/group/tests.py b/group/tests.py index 7ca1225..9af9da3 100644 --- a/group/tests.py +++ b/group/tests.py @@ -83,7 +83,7 @@ class GroupAdminAPITest(APITestCase): response = self.client.get(self.url, data=data) self.assertEqual(response.data["code"], 0) - def test_success_get_all_groups(self): + def tests_get_all_groups_successfully(self): self.assertEqual(self.client.get(self.url).data["code"], 0) @@ -128,7 +128,7 @@ class GroupMemberAdminAPITest(APITestCase): response = self.client.put(self.url, data=json.dumps(data), content_type="application/json") self.assertEqual(response.data, {"code": 1, "data": u"小组不存在"}) - def test_success_del_members(self): + def test_del_members_successfully(self): data = {"group_id": self.group.id, "members": [self.user1.id]} response = self.client.put(self.url, data=json.dumps(data), content_type="application/json") self.assertEqual(response.data, {"code": 0, "data": u"删除成功"}) @@ -166,7 +166,7 @@ class JoinGroupAPITest(APITestCase): response = self.client.post(self.url, data=data) self.assertEqual(response.data, {"code": 1, "data": u"小组不存在"}) - def test_join0(self): + def test_join0_successfully(self): data = {"group_id": self.group.id, "message": "message0"} response = self.client.post(self.url, data=data) self.assertEqual(response.data, {"code": 0, "data": u"你已经成功的加入该小组"}) @@ -176,7 +176,7 @@ class JoinGroupAPITest(APITestCase): response = self.client.post(self.url, data=data) self.assertEqual(response.data, {"code": 1, "data": u"你已经是小组成员了"}) - def test_success_join1(self): + def test_join1_successfully(self): group = self._create_group("group1", 1) data = {"group_id": group.id, "message": "message1"} response = self.client.post(self.url, data=data) @@ -186,7 +186,12 @@ class JoinGroupAPITest(APITestCase): except JoinGroupRequest.DoesNotExist: raise AssertionError() - def test_success_join2(self): + # 再提交一遍 已经提交过申请,请等待审核 + data = {"group_id": group.id, "message": "message1"} + response = self.client.post(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"你已经提交过申请了,请等待审核"}) + + def test_join2_successfully(self): group = self._create_group("group2", 2) data = {"group_id": group.id, "message": "message2"} response = self.client.post(self.url, data=data) @@ -199,3 +204,57 @@ class JoinGroupAPITest(APITestCase): def test_query_by_keyword(self): response = self.client.get(self.url + "?keyword=group0") self.assertEqual(response.data["code"], 0) + + +class JoinGroupRequestAdminAPITest(APITestCase): + def setUp(self): + self.client = APIClient() + self.url = reverse('join_group_request_admin_api') + self.user = User.objects.create(username="test1", admin_type=SUPER_ADMIN) + self.user.set_password("testaa") + self.user.save() + self.user1 = User.objects.create(username="test2") + self.user1.set_password("testbb") + self.user1.save() + self.client.login(username="test1", password="testaa") + self.group = Group.objects.create(name="group1", description="des0", + join_group_setting=1, visible="True", + admin=self.user) + self.request = JoinGroupRequest.objects.create(group=self.group, user=self.user1, + message="message1") + + + # 以下是管理的群的加群请求测试 + def test_get_all_request_successfully(self): + self.assertEqual(self.client.get(self.url).data["code"], 0) + + # 以下是同意或者拒绝加入小组请求的测试 + def test_invalid_format(self): + data = {"requested_id": self.request.id} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 1) + + def test_request_does_not_exist(self): + data = {"request_id": self.request.id + 1, "status": False} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"请求不存在"}) + + def test_request_refuse_successfully(self): + data = {"request_id": self.request.id, "status": False} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 0, "data": u"已拒绝"}) + self.assertEqual(JoinGroupRequest.objects.get(id=self.request.id).status, True) + + def test_join_group_successfully(self): + data = {"request_id": self.request.id, "status": True} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 0, "data": u"加入成功"}) + self.assertEqual(UserGroupRelation.objects.get(group=self.group).user.username, self.user1.username) + + # 再加入一次,此时返回的消息应为 加入失败,已经在本小组内 + request = JoinGroupRequest.objects.create(group=self.group, user=self.user1, + message="message2") + data = {"request_id": request.id, "status": True} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data, {"code": 1, "data": u"加入失败,已经在本小组内"}) + From 49d9a8cd0a997ae424cec4e455f4e3e74758f995 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Wed, 12 Aug 2015 15:17:27 +0800 Subject: [PATCH 129/304] =?UTF-8?q?=E4=BF=AE=E6=94=B9group/views=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E5=91=BD=E5=90=8D=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/group/views.py b/group/views.py index 9edb9c6..79ccbd4 100644 --- a/group/views.py +++ b/group/views.py @@ -185,7 +185,7 @@ class JoinGroupAPIView(APIView): def get(self, request): """ - 搜素小组的api,需要传递keyword参数 + 搜索小组的api,需要传递keyword参数 --- response_serializer: GroupSerializer """ @@ -220,7 +220,7 @@ class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase): try: join_request = JoinGroupRequest.objects.get(id=data["request_id"], group__admin=request.user, status=False) except JoinGroupRequest.DoesNotExist: - return error_response(u"小组不存在") + return error_response(u"请求不存在") join_request.status = True join_request.save() From 92ab7e5fb279e4b05a01e72dc0ebe8e61edcfed5 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 12 Aug 2015 16:49:25 +0800 Subject: [PATCH 130/304] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- judge/controller/tasks.py | 13 ----------- {judge/judger => judger}/__init__.py | 0 {judge/judger => judger}/client.py | 0 {judge/judger => judger}/compiler.py | 0 {judge/judger => judger}/judge_exceptions.py | 0 {judge/judger => judger}/language.py | 0 {judge/judger => judger}/result.py | 0 {judge/judger => judger}/run.py | 23 ++++++++++++------- {judge/judger => judger}/settings.py | 0 {judge/judger => judger}/utils.py | 0 .../README.md | 0 .../__init__.py | 0 .../celery.py | 2 +- judger_controller/tasks.py | 20 ++++++++++++++++ submission/views.py | 6 ++--- 15 files changed, 39 insertions(+), 25 deletions(-) delete mode 100644 judge/controller/tasks.py rename {judge/judger => judger}/__init__.py (100%) rename {judge/judger => judger}/client.py (100%) rename {judge/judger => judger}/compiler.py (100%) rename {judge/judger => judger}/judge_exceptions.py (100%) rename {judge/judger => judger}/language.py (100%) rename {judge/judger => judger}/result.py (100%) rename {judge/judger => judger}/run.py (70%) rename {judge/judger => judger}/settings.py (100%) rename {judge/judger => judger}/utils.py (100%) rename {judge/controller => judger_controller}/README.md (100%) rename {judge/controller => judger_controller}/__init__.py (100%) rename {judge/controller => judger_controller}/celery.py (83%) create mode 100644 judger_controller/tasks.py diff --git a/judge/controller/tasks.py b/judge/controller/tasks.py deleted file mode 100644 index 42b88ee..0000000 --- a/judge/controller/tasks.py +++ /dev/null @@ -1,13 +0,0 @@ -# coding=utf-8 -from __future__ import absolute_import -from judge.controller.celery import app -import subprocess32 as subprocess - - -@app.task -def judge(solution_id, time_limit, memory_limit, test_case_id): - subprocess.call("/usr/bin/docker run -t -i --privileged -v /var/test_case/:/var/judger/test_case/ " - "-v /var/code/:/var/judger/code/ judger python judge/judger/run.py " - "--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % - (solution_id, str(time_limit), str(memory_limit), test_case_id), - timeout=(time_limit / 100) * 20) diff --git a/judge/judger/__init__.py b/judger/__init__.py similarity index 100% rename from judge/judger/__init__.py rename to judger/__init__.py diff --git a/judge/judger/client.py b/judger/client.py similarity index 100% rename from judge/judger/client.py rename to judger/client.py diff --git a/judge/judger/compiler.py b/judger/compiler.py similarity index 100% rename from judge/judger/compiler.py rename to judger/compiler.py diff --git a/judge/judger/judge_exceptions.py b/judger/judge_exceptions.py similarity index 100% rename from judge/judger/judge_exceptions.py rename to judger/judge_exceptions.py diff --git a/judge/judger/language.py b/judger/language.py similarity index 100% rename from judge/judger/language.py rename to judger/language.py diff --git a/judge/judger/result.py b/judger/result.py similarity index 100% rename from judge/judger/result.py rename to judger/result.py diff --git a/judge/judger/run.py b/judger/run.py similarity index 70% rename from judge/judger/run.py rename to judger/run.py index 40da1dc..70d6db2 100644 --- a/judge/judger/run.py +++ b/judger/run.py @@ -10,6 +10,11 @@ from compiler import compile_ from result import result from settings import judger_workspace +import sys +import os +sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/' + '..')) +print sys.path + from oj import settings # 简单的解析命令行参数 @@ -46,12 +51,14 @@ except Exception as e: print [{"result": result["compile_error"]}] exit() -client = JudgeClient(language_code=language, - exe_path=exe_path, - max_cpu_time=int(time_limit), - max_real_time=int(time_limit) * 2, - max_memory=int(memory_limit), - test_case_dir="/var/judger/test_case/" + str(test_case_id) + "/") -print client.run() - +try: + client = JudgeClient(language_code=language, + exe_path=exe_path, + max_cpu_time=int(time_limit), + max_real_time=int(time_limit) * 2, + max_memory=int(memory_limit), + test_case_dir="/var/judger/test_case/" + str(test_case_id) + "/") + print client.run() +except Exception as e: + print e diff --git a/judge/judger/settings.py b/judger/settings.py similarity index 100% rename from judge/judger/settings.py rename to judger/settings.py diff --git a/judge/judger/utils.py b/judger/utils.py similarity index 100% rename from judge/judger/utils.py rename to judger/utils.py diff --git a/judge/controller/README.md b/judger_controller/README.md similarity index 100% rename from judge/controller/README.md rename to judger_controller/README.md diff --git a/judge/controller/__init__.py b/judger_controller/__init__.py similarity index 100% rename from judge/controller/__init__.py rename to judger_controller/__init__.py diff --git a/judge/controller/celery.py b/judger_controller/celery.py similarity index 83% rename from judge/controller/celery.py rename to judger_controller/celery.py index 8e776e2..d7e0d47 100644 --- a/judge/controller/celery.py +++ b/judger_controller/celery.py @@ -2,4 +2,4 @@ from __future__ import absolute_import from celery import Celery -app = Celery("judge", broker="redis://localhost:6379/0", include=["judge.controller.tasks"]) \ No newline at end of file +app = Celery("judge", broker="redis://localhost:6379/0", include=["judger_controller.tasks"]) \ No newline at end of file diff --git a/judger_controller/tasks.py b/judger_controller/tasks.py new file mode 100644 index 0000000..9ac6007 --- /dev/null +++ b/judger_controller/tasks.py @@ -0,0 +1,20 @@ +# coding=utf-8 +from __future__ import absolute_import +from judger_controller.celery import app +import subprocess32 as subprocess + + +@app.task +def judge(solution_id, time_limit, memory_limit, test_case_id): + try: + subprocess.call("docker run -t -i --privileged --rm=true " + "-v /var/test_case/:/var/judger/test_case/ " + "-v /Users/virusdefender/Desktop/:/var/judger/code/ " + "-p 27017:27017 " + "judger " + "python judger/run.py " + "--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % + (solution_id, str(time_limit), str(memory_limit), test_case_id), + timeout=(time_limit / 100) * 20, shell=True) + except subprocess.TimeoutExpired: + print "docker timeout" diff --git a/submission/views.py b/submission/views.py index 1208632..993de50 100644 --- a/submission/views.py +++ b/submission/views.py @@ -8,8 +8,8 @@ from rest_framework.views import APIView from django.conf import settings -from judge.judger.result import result -from judge.controller.tasks import judge +from judger.result import result +from judger_controller.tasks import judge from account.decorators import login_required from problem.models import Problem from utils.shortcuts import serializer_invalid_response, error_response, success_response @@ -42,7 +42,7 @@ class SubmissionnAPIView(APIView): connection = pymongo.MongoClient(host=mongodb_setting["HOST"], port=mongodb_setting["PORT"]) collection = connection["oj"]["oj_submission"] submission_id = str(collection.insert_one(data).inserted_id) - judge.delay(submission_id, problem.max_cpu_time, problem.max_memory, problem.test_case_id) + judge.delay(submission_id, problem.time_limit, problem.memory_limit, problem.test_case_id) return success_response({"submission_id": submission_id}) else: return serializer_invalid_response(serializer) From 6153aa4002113cf189612f825bd3613a248fdcf7 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Wed, 12 Aug 2015 18:11:00 +0800 Subject: [PATCH 131/304] =?UTF-8?q?=E5=89=8D=E7=AB=AF=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B0=8F=E7=BB=84=E4=BF=A1=E6=81=AF=E9=83=A8?= =?UTF-8?q?=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group/migrations/0004_merge.py | 15 ++++ static/src/js/app/admin/group/group_detail.js | 73 ++++++++++++++----- template/admin/group/group_detail.html | 15 ++-- template/admin/user/user_list.html | 2 +- 4 files changed, 75 insertions(+), 30 deletions(-) create mode 100644 group/migrations/0004_merge.py diff --git a/group/migrations/0004_merge.py b/group/migrations/0004_merge.py new file mode 100644 index 0000000..febb5e2 --- /dev/null +++ b/group/migrations/0004_merge.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0003_auto_20150811_1906'), + ('group', '0002_auto_20150811_1649'), + ] + + operations = [ + ] diff --git a/static/src/js/app/admin/group/group_detail.js b/static/src/js/app/admin/group/group_detail.js index 0c6561e..e946ba2 100644 --- a/static/src/js/app/admin/group/group_detail.js +++ b/static/src/js/app/admin/group/group_detail.js @@ -13,28 +13,8 @@ require(["jquery", "avalon", "csrf", "bs_alert", "validation"], function ($, ava page_count: 1, name: "", description: "", - join_group_setting: {0: false, 1: false, 2: false}, checked_setting: "0", - updateGroupInfo: function () { - $.ajax({ - beforeSend: csrfHeader, - url: "/api/admin/group/", - method: "put", - data: {group_id: avalon.vmodels.admin.group_id, name: vm.name, - description: vm.description, join_group_setting: vm.checked_setting}, - dataType: "json", - success: function (data) { - if (!data.code) { - bs_alert("修改成功"); - } - else { - bs_alert(data.data); - } - } - }) - }, - getNext: function () { if (!vm.next_page) return; @@ -110,6 +90,59 @@ require(["jquery", "avalon", "csrf", "bs_alert", "validation"], function ($, ava } } }) + + $("#edit_group_form") + .formValidation({ + framework: "bootstrap", + fields: { + name: { + validators: { + notEmpty: { + message: "请填写小组名" + }, + stringLength: { + max: 20, + message: '小组名长度必须在20位之内' + } + } + }, + description: { + validators: { + notEmpty: { + message: "请填写描述" + }, + stringLength: { + max: 300, + message: '描述长度必须在300位之内' + } + } + } + } + } + ).on('success.form.fv', function (e) { + e.preventDefault(); + var data = { + group_id: avalon.vmodels.admin.group_id, + name: vm.name, + description: vm.description, + join_group_setting: vm.checked_setting + }; + $.ajax({ + beforeSend: csrfHeader, + url: "/api/admin/group/", + method: "put", + data: data, + dataType: "json", + success: function (data) { + if (!data.code) { + bs_alert("修改成功"); + } + else { + bs_alert(data.data); + } + } + }) + }); }); }); \ No newline at end of file diff --git a/template/admin/group/group_detail.html b/template/admin/group/group_detail.html index dc695e0..7c4559f 100644 --- a/template/admin/group/group_detail.html +++ b/template/admin/group/group_detail.html @@ -25,11 +25,9 @@
-

修改小组信息

-
- +
@@ -37,22 +35,21 @@
- +
+ 允许任何人加入 提交请求后管理员审核 不允许任何人加入 +
-
-
- +
-
- +
\ No newline at end of file diff --git a/template/admin/user/user_list.html b/template/admin/user/user_list.html index 14d020d..a281cd3 100644 --- a/template/admin/user/user_list.html +++ b/template/admin/user/user_list.html @@ -71,7 +71,7 @@
- +
From 890bbc22a279de13bed3d5abc3dc2da76ff94b7e Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Wed, 12 Aug 2015 18:13:04 +0800 Subject: [PATCH 132/304] =?UTF-8?q?=E4=BF=AE=E6=94=B9group=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E4=B8=AD=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group/tests.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/group/tests.py b/group/tests.py index 9af9da3..f62dfa5 100644 --- a/group/tests.py +++ b/group/tests.py @@ -181,10 +181,7 @@ class JoinGroupAPITest(APITestCase): data = {"group_id": group.id, "message": "message1"} response = self.client.post(self.url, data=data) self.assertEqual(response.data, {"code": 0, "data": u"申请提交成功,请等待审核"}) - try: - JoinGroupRequest.objects.get(user=self.user, group=group, status=False) - except JoinGroupRequest.DoesNotExist: - raise AssertionError() + JoinGroupRequest.objects.get(user=self.user, group=group, status=False) # 再提交一遍 已经提交过申请,请等待审核 data = {"group_id": group.id, "message": "message1"} @@ -223,7 +220,6 @@ class JoinGroupRequestAdminAPITest(APITestCase): self.request = JoinGroupRequest.objects.create(group=self.group, user=self.user1, message="message1") - # 以下是管理的群的加群请求测试 def test_get_all_request_successfully(self): self.assertEqual(self.client.get(self.url).data["code"], 0) @@ -249,7 +245,7 @@ class JoinGroupRequestAdminAPITest(APITestCase): data = {"request_id": self.request.id, "status": True} response = self.client.put(self.url, data=data) self.assertEqual(response.data, {"code": 0, "data": u"加入成功"}) - self.assertEqual(UserGroupRelation.objects.get(group=self.group).user.username, self.user1.username) + UserGroupRelation.objects.get(group=self.group, user=self.user1) # 再加入一次,此时返回的消息应为 加入失败,已经在本小组内 request = JoinGroupRequest.objects.create(group=self.group, user=self.user1, From 66d3cd3bfeda059e80eee605a08c6d7db13f77b1 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 12 Aug 2015 19:55:41 +0800 Subject: [PATCH 133/304] =?UTF-8?q?=E5=88=A4=E9=A2=98=E6=9A=82=E6=97=B6?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E8=B7=91=E8=B5=B7=E6=9D=A5=E4=BA=86=EF=BC=8C?= =?UTF-8?q?=E7=9C=9F=E8=A6=81=E8=A2=AB=E8=87=AA=E5=B7=B1=E8=A0=A2=E5=93=AD?= =?UTF-8?q?=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/__init__.py | 1 - config/config.py | 2 -- judger/client.py | 2 +- judger/compiler.py | 1 - judger/language.py | 4 ++-- judger/run.py | 23 +++++++---------------- judger/settings.py | 9 ++++++++- judger_controller/celery.py | 7 ++++++- judger_controller/settings.py | 6 ++++++ judger_controller/tasks.py | 6 +++--- oj/local_settings.py | 4 +++- static/src/js/app/oj/problem/problem.js | 8 ++++++-- submission/views.py | 1 + 13 files changed, 43 insertions(+), 31 deletions(-) delete mode 100644 config/__init__.py delete mode 100644 config/config.py create mode 100644 judger_controller/settings.py diff --git a/config/__init__.py b/config/__init__.py deleted file mode 100644 index 9bad579..0000000 --- a/config/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# coding=utf-8 diff --git a/config/config.py b/config/config.py deleted file mode 100644 index 89d316b..0000000 --- a/config/config.py +++ /dev/null @@ -1,2 +0,0 @@ -# coding=utf-8 -DB_NAME = "db.sqlite3" diff --git a/judger/client.py b/judger/client.py index d159583..52f6586 100644 --- a/judger/client.py +++ b/judger/client.py @@ -30,7 +30,7 @@ class JudgeClient(object): :param test_case_dir: 测试用例文件夹路径 :return:返回结果list """ - self._language = languages[str(language_code)] + self._language = languages[language_code] self._exe_path = exe_path self._max_cpu_time = max_cpu_time self._max_real_time = max_real_time diff --git a/judger/compiler.py b/judger/compiler.py index d4bca4b..7f40ff1 100644 --- a/judger/compiler.py +++ b/judger/compiler.py @@ -32,5 +32,4 @@ def compile_(language_item, src_path, exe_path): if parse_result["exit_code"] or parse_result["term_sig"] or parse_result["siginaled"] or parse_result["exceed"]: raise CompileError("Compile error") - return exe_path diff --git a/judger/language.py b/judger/language.py index c79b296..a61bd32 100644 --- a/judger/language.py +++ b/judger/language.py @@ -6,14 +6,14 @@ languages = { "name": "c", "src_name": "main.c", "code": 1, - "compile_command": "gcc -DONLINE_JUDGE -O2 -w -std=c99 -pipe {src_path} -lm -o {exe_path}main", + "compile_command": "gcc -DONLINE_JUDGE -O2 -w -std=c99 {src_path} -lm -o {exe_path}main", "execute_command": "{exe_path}main" }, 2: { "name": "cpp", "src_name": "main.cpp", "code": 2, - "compile_command": "g++ -DONLINE_JUDGE -O2 -w -std=c++11 -pipe {src_path} -lm -o {exe_path}main", + "compile_command": "g++ -DONLINE_JUDGE -O2 -w -std=c++11 {src_path} -lm -o {exe_path}main", "execute_command": "{exe_path}main" }, 3: { diff --git a/judger/run.py b/judger/run.py index 70d6db2..1e83bb8 100644 --- a/judger/run.py +++ b/judger/run.py @@ -8,14 +8,8 @@ from client import JudgeClient from language import languages from compiler import compile_ from result import result -from settings import judger_workspace +from settings import judger_workspace, mongodb_config -import sys -import os -sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/' + '..')) -print sys.path - -from oj import settings # 简单的解析命令行参数 # 参数有 -solution_id -time_limit -memory_limit -test_case_id @@ -26,16 +20,13 @@ time_limit = args[4] memory_limit = args[6] test_case_id = args[8] - -mongodb_setting = settings.DATABASES["mongodb"] -connection = pymongo.MongoClient(host=mongodb_setting["HOST"], port=mongodb_setting["PORT"]) +connection = pymongo.MongoClient(host=mongodb_config["host"], port=mongodb_config["port"]) collection = connection["oj"]["oj_submission"] submission = collection.find_one({"_id": ObjectId(solution_id)}) if not submission: exit() - # 将代码写入文件 language = languages[submission["language"]] src_path = judger_workspace + "run/" + language["src_name"] @@ -47,18 +38,18 @@ f.close() try: exe_path = compile_(language, src_path, judger_workspace + "run/") except Exception as e: - print e - print [{"result": result["compile_error"]}] + print {"result": result["compile_error"]} exit() - try: - client = JudgeClient(language_code=language, + client = JudgeClient(language_code=submission["language"], exe_path=exe_path, max_cpu_time=int(time_limit), max_real_time=int(time_limit) * 2, max_memory=int(memory_limit), - test_case_dir="/var/judger/test_case/" + str(test_case_id) + "/") + test_case_dir= judger_workspace + "test_case/" + test_case_id + "/") print client.run() except Exception as e: print e + print {"result": result["system_error"]} + diff --git a/judger/settings.py b/judger/settings.py index 8fea94b..b70f454 100644 --- a/judger/settings.py +++ b/judger/settings.py @@ -11,5 +11,12 @@ lrun_uid = 1001 # lrun用户组gid lrun_gid = 1002 -#judger工作目录 +# judger工作目录 judger_workspace = "/var/judger/" + +mongodb_config = { + "host": "192.168.59.3", + "username": "root", + "password": "root", + "port": 27017 +} diff --git a/judger_controller/celery.py b/judger_controller/celery.py index d7e0d47..35ba2ac 100644 --- a/judger_controller/celery.py +++ b/judger_controller/celery.py @@ -1,5 +1,10 @@ # coding=utf-8 from __future__ import absolute_import from celery import Celery +from .settings import redis_config -app = Celery("judge", broker="redis://localhost:6379/0", include=["judger_controller.tasks"]) \ No newline at end of file +app = Celery("judge", broker="redis://" + + redis_config["host"] + ":" + + str(redis_config["port"]) + + "/" + str(redis_config["db"]), + include=["judger_controller.tasks"]) diff --git a/judger_controller/settings.py b/judger_controller/settings.py new file mode 100644 index 0000000..4d492eb --- /dev/null +++ b/judger_controller/settings.py @@ -0,0 +1,6 @@ +# coding=utf-8 +redis_config = { + "host": "127.0.0.1", + "port": 6379, + "db": 0 +} \ No newline at end of file diff --git a/judger_controller/tasks.py b/judger_controller/tasks.py index 9ac6007..054452c 100644 --- a/judger_controller/tasks.py +++ b/judger_controller/tasks.py @@ -8,13 +8,13 @@ import subprocess32 as subprocess def judge(solution_id, time_limit, memory_limit, test_case_id): try: subprocess.call("docker run -t -i --privileged --rm=true " - "-v /var/test_case/:/var/judger/test_case/ " + "-v /Users/virusdefender/Desktop/test_case/:/var/judger/test_case/ " "-v /Users/virusdefender/Desktop/:/var/judger/code/ " - "-p 27017:27017 " "judger " "python judger/run.py " "--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % (solution_id, str(time_limit), str(memory_limit), test_case_id), - timeout=(time_limit / 100) * 20, shell=True) + # 如果设置的最长运行时间小于1000毫秒,那么/1000就是0,处理一下这个情况,设置为两秒 + timeout=(time_limit / 1000 * 3) or 2, shell=True) except subprocess.TimeoutExpired: print "docker timeout" diff --git a/oj/local_settings.py b/oj/local_settings.py index 2f38496..eff9818 100644 --- a/oj/local_settings.py +++ b/oj/local_settings.py @@ -7,6 +7,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 下面是需要自己修改的 LOG_PATH = "LOG/" +# 注意这是web 服务器访问的地址,判题度武器访问的地址不一定一样,因为可能不在一台机器上 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', @@ -24,5 +25,6 @@ DATABASES = { DEBUG = True -TEST_CASE_DIR = "/var/test_case/" +# 同理 这是 web 服务器的上传路径 +TEST_CASE_DIR = "/Users/virusdefender/Desktop/test_case/" diff --git a/static/src/js/app/oj/problem/problem.js b/static/src/js/app/oj/problem/problem.js index 5b10211..fd544cf 100644 --- a/static/src/js/app/oj/problem/problem.js +++ b/static/src/js/app/oj/problem/problem.js @@ -1,4 +1,4 @@ -require(["jquery", "code_mirror"], function ($, code_mirror) { +require(["jquery", "code_mirror", "csrf"], function ($, code_mirror, csrfHeader) { var code_editor = code_mirror($("#code-editor")[0], "text/x-csrc"); var language = "1"; @@ -21,7 +21,11 @@ require(["jquery", "code_mirror"], function ($, code_mirror) { $("#submit-code-button").click(function () { show_loading(); $.ajax({ - + beforeSend: csrfHeader, + url: "/api/submission/", + method: "post", + data: JSON.stringify({problem_id: 2, language: language, code: code_editor.getValue()}), + contentType: "application/json" }); setTimeout( function () { diff --git a/submission/views.py b/submission/views.py index 993de50..7d2ef23 100644 --- a/submission/views.py +++ b/submission/views.py @@ -32,6 +32,7 @@ class SubmissionnAPIView(APIView): serializer = CreateSubmissionSerializer(data=request.data) if serializer.is_valid(): data = serializer.data + # data["language"] = int(data["language"]) data["user_id"] = request.user.id data["result"] = result["waiting"] try: From f6c3ceee40917129cadbedc1cb9d13a5a943e0d2 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 12 Aug 2015 22:59:05 +0800 Subject: [PATCH 134/304] =?UTF-8?q?=E5=88=A0=E9=99=A4=20ManyToMany=20?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E6=97=A0=E6=95=88=E7=9A=84=20null=3DTrue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problem/migrations/0004_auto_20150812_2254.py | 19 +++++++++++++++++++ problem/models.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 problem/migrations/0004_auto_20150812_2254.py diff --git a/problem/migrations/0004_auto_20150812_2254.py b/problem/migrations/0004_auto_20150812_2254.py new file mode 100644 index 0000000..75f85d4 --- /dev/null +++ b/problem/migrations/0004_auto_20150812_2254.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('problem', '0003_auto_20150810_2233'), + ] + + operations = [ + migrations.AlterField( + model_name='problem', + name='tags', + field=models.ManyToManyField(to='problem.ProblemTag'), + ), + ] diff --git a/problem/models.py b/problem/models.py index b9a3181..aaa5ce0 100644 --- a/problem/models.py +++ b/problem/models.py @@ -49,4 +49,4 @@ class Problem(AbstractProblem): # 难度 0 - n difficulty = models.IntegerField() # 标签 - tags = models.ManyToManyField(ProblemTag, null=True) + tags = models.ManyToManyField(ProblemTag) From 41eec53430d5bb85f3f1a40ef949534c04aca329 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 12 Aug 2015 22:59:27 +0800 Subject: [PATCH 135/304] =?UTF-8?q?=E5=90=88=E5=B9=B6=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/views.py | 59 ++++++++++++++--------- static/src/js/app/admin/user/user_list.js | 2 +- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/account/views.py b/account/views.py index fe14464..f053903 100644 --- a/account/views.py +++ b/account/views.py @@ -7,9 +7,11 @@ from rest_framework.views import APIView from utils.shortcuts import serializer_invalid_response, error_response, success_response, paginate +from .decorators import login_required from .models import User -from .serializers import UserLoginSerializer, UsernameCheckSerializer, UserRegisterSerializer, \ - UserChangePasswordSerializer, EmailCheckSerializer, UserSerializer, EditUserSerializer +from .serializers import (UserLoginSerializer, UsernameCheckSerializer, + UserRegisterSerializer, UserChangePasswordSerializer, + EmailCheckSerializer, UserSerializer, EditUserSerializer) class UserLoginAPIView(APIView): @@ -118,28 +120,6 @@ class EmailCheckAPIView(APIView): return serializer_invalid_response(serializer) -class UserAPIView(APIView): - def get(self, request): - """ - 用户分页json api接口 - --- - response_serializer: UserSerializer - """ - user = User.objects.all().order_by("-create_time") - admin_type = request.GET.get("admin_type", None) - if admin_type: - try: - user = user.filter(admin_type__gte=int(admin_type)) - except ValueError: - return error_response(u"参数错误") - keyword = request.GET.get("keyword", None) - if keyword: - user = user.filter(Q(username__contains=keyword) | - Q(real_name__contains=keyword) | - Q(email__contains=keyword)) - return paginate(request, user, UserSerializer) - - class UserAdminAPIView(APIView): def put(self, request): """ @@ -165,3 +145,34 @@ class UserAdminAPIView(APIView): return success_response(UserSerializer(user).data) else: return serializer_invalid_response(serializer) + + def get(self, request): + """ + 用户分页json api接口 + --- + response_serializer: UserSerializer + """ + user = User.objects.all().order_by("-create_time") + admin_type = request.GET.get("admin_type", None) + if admin_type: + try: + user = user.filter(admin_type__gte=int(admin_type)) + except ValueError: + return error_response(u"参数错误") + keyword = request.GET.get("keyword", None) + if keyword: + user = user.filter(Q(username__contains=keyword) | + Q(real_name__contains=keyword) | + Q(email__contains=keyword)) + return paginate(request, user, UserSerializer) + + +class UserInfoAPIView(APIView): + @login_required + def get(self, request): + """ + 返回这个用户的个人信息 + --- + response_serializer: UserSerializer + """ + return success_response(UserSerializer(request.user).data) diff --git a/static/src/js/app/admin/user/user_list.js b/static/src/js/app/admin/user/user_list.js index 910c24a..1e5f254 100644 --- a/static/src/js/app/admin/user/user_list.js +++ b/static/src/js/app/admin/user/user_list.js @@ -63,7 +63,7 @@ require(["jquery", "avalon", "csrf", "bs_alert", "validation"], function ($, ava getPageData(1); //用户列表初始化 //Ajax get数据 function getPageData(page) { - var url = "/api/admin/users/?paging=true&page=" + page + "&page_size=10"; + var url = "/api/admin/user/?paging=true&page=" + page + "&page_size=10"; if (vm.showAdminOnly == true) url += "&admin_type=1"; if (vm.key_word != "") From 10ad8f85b5a9a2b1febaa570b5025a0d62bca29d Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 12 Aug 2015 22:59:36 +0800 Subject: [PATCH 136/304] =?UTF-8?q?=E6=95=B4=E7=90=86=20url=20=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/oj/urls.py b/oj/urls.py index 5c7171f..3165c1a 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -5,26 +5,28 @@ from django.views.generic import TemplateView from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView, EmailCheckAPIView, - UserAPIView, UserAdminAPIView) + UserAdminAPIView, UserInfoAPIView) from announcement.views import AnnouncementAPIView, AnnouncementAdminAPIView -from group.views import GroupAdminAPIView, GroupMemberAdminAPIView, JoinGroupAPIView, JoinGroupRequestAdminAPIView +from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView, + JoinGroupAPIView, JoinGroupRequestAdminAPIView) from admin.views import AdminTemplateView -from problem.views import ProblemAdminAPIView -from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView +from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView from submission.views import SubmissionnAPIView - urlpatterns = [ url(r'^install/$', "install.views.install"), 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/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"), + 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/user/$', UserInfoAPIView.as_view(), name="user_info_api"), 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"), @@ -33,24 +35,27 @@ 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'^announcement/(?P\d+)/$', "announcement.views.announcement_page", + name="announcement_page"), url(r'^api/announcements/$', AnnouncementAPIView.as_view(), name="announcement_list_api"), - url(r'^api/admin/users/$', UserAPIView.as_view(), name="user_list_api"), - - 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"), + 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"), url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"), 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/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_solutions/', "problem.views.problem_my_solutions_list_page", name="problem_my_solutions_page"), + url(r'^problem/(?P\d+)/my_solutions/', "problem.views.problem_my_solutions_list_page", + name="problem_my_solutions_page"), url(r'^my_solution/(?P\d+)/$', "problem.views.my_solution", name="my_solution_page"), - url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), name="join_group_request_admin_api"), + url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), + name="join_group_request_admin_api"), url(r'^api/submission/$', SubmissionnAPIView.as_view(), name="submission_api"), ] From 309401f7550afe44d50a26f1dcdc0e474290f32b Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 12 Aug 2015 22:59:50 +0800 Subject: [PATCH 137/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=B0=8F=E7=BB=84?= =?UTF-8?q?=E7=AD=9B=E9=80=89=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group/views.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/group/views.py b/group/views.py index 79ccbd4..a9ec8d2 100644 --- a/group/views.py +++ b/group/views.py @@ -94,6 +94,7 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase): response_serializer: GroupSerializer """ group_id = request.GET.get("group_id", None) + # 根据 id 查询小组信息 if group_id: try: group = self.get_group(request, group_id) @@ -102,8 +103,15 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase): return error_response(u"小组不存在") else: groups = self.get_groups(request) + # 搜索小组 if request.GET.get("keyword", None): groups = groups.filter(name__contains=request.GET["keyword"]) + # 只返回我创建的小组 适用于超级管理员 + if request.GET.get("my_group", None): + groups = groups.filter(admin=request.user) + # 只返回指定用户的小组 适用于管理员 + elif request.GET.get("admin_id", None): + groups = groups.filter(admin__id=request.GET["admin_id"]) return paginate(request, groups, GroupSerializer) From 5273e9a48d04e0d36c2a0eb328fbd1104c33cdda Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 12 Aug 2015 23:18:22 +0800 Subject: [PATCH 138/304] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20decorator=20?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=20swagger=20=E6=97=A0=E6=B3=95=E8=AF=86?= =?UTF-8?q?=E5=88=AB=E6=B3=A8=E9=87=8A=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/decorators.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/account/decorators.py b/account/decorators.py index 01cdd68..ff1b5b0 100644 --- a/account/decorators.py +++ b/account/decorators.py @@ -1,4 +1,5 @@ # coding=utf-8 +from functools import wraps from django.http import HttpResponse from django.shortcuts import render @@ -7,6 +8,7 @@ from .models import User def login_required(func): + @wraps(func) def check(*args, **kwargs): # 在class based views 里面,args 有两个元素,一个是self, 第二个才是request, # 在function based views 里面,args 只有request 一个参数 @@ -21,6 +23,7 @@ def login_required(func): def admin_required(func): + @wraps(func) def check(*args, **kwargs): request = args[-1] if request.user.is_authenticated() and request.user.admin_type: From d8d91b7ecd61d4525b02f75e29d02fef895e7039 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Thu, 13 Aug 2015 15:15:53 +0800 Subject: [PATCH 139/304] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E8=BE=93=E5=85=A5=E6=8F=8F=E8=BF=B0=E4=B8=8E=E8=BE=93=E5=87=BA?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E4=B8=A4=E4=B8=AA=E5=AD=97=E6=AE=B5=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E4=BF=AE=E6=94=B9=E5=85=B6=E7=9B=B8=E5=85=B3=E7=9A=84?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problem/models.py | 4 ++++ problem/serizalizers.py | 4 ++++ problem/tests.py | 16 +++++++++++----- problem/views.py | 4 ++++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/problem/models.py b/problem/models.py index b9a3181..82b99f6 100644 --- a/problem/models.py +++ b/problem/models.py @@ -16,6 +16,10 @@ class AbstractProblem(models.Model): title = models.CharField(max_length=50) # 问题描述 HTML 格式 description = models.TextField() + # 输入描述 + description_input = models.CharField(max_length=10000) + # 输出描述 + description_output = models.CharField(max_length=10000) # 样例输入 可能会存储 json 格式的数据 samples = models.TextField(blank=True) # 测试用例id 这个id 可以用来拼接得到测试用例的文件存储位置 diff --git a/problem/serizalizers.py b/problem/serizalizers.py index b6b9faf..989c068 100644 --- a/problem/serizalizers.py +++ b/problem/serizalizers.py @@ -20,6 +20,8 @@ class JSONField(serializers.Field): class CreateProblemSerializer(serializers.Serializer): title = serializers.CharField(max_length=50) description = serializers.CharField(max_length=10000) + description_input = serializers.CharField(max_length=10000) + description_output = serializers.CharField(max_length=10000) # [{"input": "1 1", "output": "2"}] samples = ProblemSampleSerializer() test_case_id = serializers.CharField(max_length=40) @@ -49,6 +51,8 @@ class EditProblemSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=50) description = serializers.CharField(max_length=10000) + description_input = serializers.CharField(max_length=10000) + description_output = serializers.CharField(max_length=10000) test_case_id = serializers.CharField(max_length=40) source = serializers.CharField(max_length=30) time_limit = serializers.IntegerField() diff --git a/problem/tests.py b/problem/tests.py index 708d7f2..8838cce 100644 --- a/problem/tests.py +++ b/problem/tests.py @@ -17,8 +17,10 @@ class ProblemPageTest(TestCase): class ProblemAdminTest(APITestCase): def _create_data(self, problem_id, visible, tags): data = {"id": problem_id, - "title": "title1", - "description": "des1", + "title": "title0", + "description": "description0", + "description_input": "description_input0", + "description_output": "description_output0", "test_case_id": "1", "source": "source1", "samples": [{"input": "1 1", "output": "2"}], @@ -40,7 +42,9 @@ class ProblemAdminTest(APITestCase): ProblemTag.objects.create(name="tag1") ProblemTag.objects.create(name="tag2") self.problem = Problem.objects.create(title="title1", - description="des1", + description="description1", + description_input="description_input1", + description_output="description_output1", test_case_id="1", source="source1", samples=[{"input": "1 1", "output": "2"}], @@ -57,8 +61,10 @@ class ProblemAdminTest(APITestCase): self.assertEqual(response.data["code"], 1) def test_release_problem_successfully(self): - data = {"title": "title1", - "description": "des1", + data = {"title": "title2", + "description": "description2", + "description_input": "description_input2", + "description_output": "description_output2", "test_case_id": "1", "source": "source1", "samples": [{"input": "1 1", "output": "2"}], diff --git a/problem/views.py b/problem/views.py index f9677b1..73fc81a 100644 --- a/problem/views.py +++ b/problem/views.py @@ -74,6 +74,8 @@ class ProblemAdminAPIView(APIView): data = serializer.data problem = Problem.objects.create(title=data["title"], description=data["description"], + description_input=data["description_input"], + description_output=data["description_output"], test_case_id=data["test_case_id"], source=data["source"], samples=json.dumps(data["samples"]), @@ -110,6 +112,8 @@ class ProblemAdminAPIView(APIView): problem.title = data["title"] problem.description = data["description"] + problem.description_input = data["description_input"] + problem.description_output = data["description_output"] problem.test_case_id = data["test_case_id"] problem.source = data["source"] problem.time_limit = data["time_limit"] From a36dd1d267f73f2ef8134f69a99ef00e118ce44e Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Thu, 13 Aug 2015 18:03:38 +0800 Subject: [PATCH 140/304] =?UTF-8?q?=E5=89=8D=E7=AB=AF=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E9=A2=98=E7=9B=AE=E7=95=8C=E9=9D=A2=E5=A2=9E=E5=8A=A0=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E6=8F=8F=E8=BF=B0=E5=92=8C=E8=BE=93=E5=87=BA=E6=8F=8F?= =?UTF-8?q?=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problem/migrations/0004_auto_20150813_1459.py | 26 +++++++++++++++++ .../src/js/app/admin/problem/add_problem.js | 28 ++++++++++++++----- template/admin/problem/add_problem.html | 14 ++++++++-- 3 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 problem/migrations/0004_auto_20150813_1459.py diff --git a/problem/migrations/0004_auto_20150813_1459.py b/problem/migrations/0004_auto_20150813_1459.py new file mode 100644 index 0000000..387a87c --- /dev/null +++ b/problem/migrations/0004_auto_20150813_1459.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('problem', '0003_auto_20150810_2233'), + ] + + operations = [ + migrations.AddField( + model_name='problem', + name='description_input', + field=models.CharField(default='hello', max_length=10000), + preserve_default=False, + ), + migrations.AddField( + model_name='problem', + name='description_output', + field=models.CharField(default='hello', max_length=10000), + preserve_default=False, + ), + ] diff --git a/static/src/js/app/admin/problem/add_problem.js b/static/src/js/app/admin/problem/add_problem.js index d2eda56..ebb661e 100644 --- a/static/src/js/app/admin/problem/add_problem.js +++ b/static/src/js/app/admin/problem/add_problem.js @@ -53,10 +53,17 @@ require(["jquery", "avalon", "editor", "uploader", "bs_alert", "csrf", "tagEdito } } }, - source: { + input_description: { validators: { notEmpty: { - message: "请输入题目来源" + message: "请填写输入描述" + } + } + }, + output_description: { + validators: { + notEmpty: { + message: "请填写输出描述" } } } @@ -72,10 +79,6 @@ require(["jquery", "avalon", "editor", "uploader", "bs_alert", "csrf", "tagEdito bs_alert("题目描述不能为空!"); return; } - if (vm.hint == '') { - bs_alert("提示不能为空!"); - return; - } var ajaxData = { title: vm.title, description: vm.description, @@ -86,6 +89,8 @@ require(["jquery", "avalon", "editor", "uploader", "bs_alert", "csrf", "tagEdito hint: vm.hint, source: vm.source, tags: $("#tags").tagEditor("getTags")[0].tags, + input_description: vm.input_description, + output_description: vm.output_description, difficulty: vm.difficulty }; if (vm.samples.length == 0) { @@ -93,6 +98,13 @@ require(["jquery", "avalon", "editor", "uploader", "bs_alert", "csrf", "tagEdito return; } + for(var i = 0; i < vm.samples.length; i++){ + if (vm.samples[i].input == "" || vm.samples[i].output == ""){ + bs_alert("样例输入与样例输出不能为空!"); + return; + } + } + if (tags.length == 0) { bs_alert("请至少添加一个标签,这将有利于用户发现你的题目!"); return; @@ -147,12 +159,14 @@ require(["jquery", "avalon", "editor", "uploader", "bs_alert", "csrf", "tagEdito description: "", cpu: 1000, memory: 256, - samples: [], + samples: [{input: "", output: "", "visible": true}], hint: "", visible: true, difficulty: 0, tags: [], tag: "", + input_description: "", + output_description: "", test_case_id: "", testCaseList: [], uploadSuccess: false, diff --git a/template/admin/problem/add_problem.html b/template/admin/problem/add_problem.html index 0b952c9..a6cf14c 100644 --- a/template/admin/problem/add_problem.html +++ b/template/admin/problem/add_problem.html @@ -3,12 +3,12 @@
- +
- + 请填写题目描述
@@ -38,6 +38,16 @@
+
+
+ +
+
+
+ +

添加 From 692a0398015194305710f9383d463f68b5f76c71 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Thu, 13 Aug 2015 18:08:51 +0800 Subject: [PATCH 141/304] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=90=8E=E7=AB=AFpro?= =?UTF-8?q?blem=E4=B8=AD=E7=9A=84=E7=9A=84=E5=91=BD=E5=90=8D=E8=A7=84?= =?UTF-8?q?=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problem/migrations/0005_auto_20150813_1807.py | 24 +++++++++++++++++++ problem/models.py | 4 ++-- problem/serizalizers.py | 8 +++---- problem/tests.py | 12 +++++----- problem/views.py | 8 +++---- 5 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 problem/migrations/0005_auto_20150813_1807.py diff --git a/problem/migrations/0005_auto_20150813_1807.py b/problem/migrations/0005_auto_20150813_1807.py new file mode 100644 index 0000000..7d50f75 --- /dev/null +++ b/problem/migrations/0005_auto_20150813_1807.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('problem', '0004_auto_20150813_1459'), + ] + + operations = [ + migrations.RenameField( + model_name='problem', + old_name='description_input', + new_name='input_description', + ), + migrations.RenameField( + model_name='problem', + old_name='description_output', + new_name='output_description', + ), + ] diff --git a/problem/models.py b/problem/models.py index 82b99f6..3be3783 100644 --- a/problem/models.py +++ b/problem/models.py @@ -17,9 +17,9 @@ class AbstractProblem(models.Model): # 问题描述 HTML 格式 description = models.TextField() # 输入描述 - description_input = models.CharField(max_length=10000) + input_description = models.CharField(max_length=10000) # 输出描述 - description_output = models.CharField(max_length=10000) + output_description = models.CharField(max_length=10000) # 样例输入 可能会存储 json 格式的数据 samples = models.TextField(blank=True) # 测试用例id 这个id 可以用来拼接得到测试用例的文件存储位置 diff --git a/problem/serizalizers.py b/problem/serizalizers.py index 989c068..d7a1856 100644 --- a/problem/serizalizers.py +++ b/problem/serizalizers.py @@ -20,8 +20,8 @@ class JSONField(serializers.Field): class CreateProblemSerializer(serializers.Serializer): title = serializers.CharField(max_length=50) description = serializers.CharField(max_length=10000) - description_input = serializers.CharField(max_length=10000) - description_output = 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 = ProblemSampleSerializer() test_case_id = serializers.CharField(max_length=40) @@ -51,8 +51,8 @@ class EditProblemSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=50) description = serializers.CharField(max_length=10000) - description_input = serializers.CharField(max_length=10000) - description_output = 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) source = serializers.CharField(max_length=30) time_limit = serializers.IntegerField() diff --git a/problem/tests.py b/problem/tests.py index 8838cce..1823655 100644 --- a/problem/tests.py +++ b/problem/tests.py @@ -19,8 +19,8 @@ class ProblemAdminTest(APITestCase): data = {"id": problem_id, "title": "title0", "description": "description0", - "description_input": "description_input0", - "description_output": "description_output0", + "input_description": "input_description0", + "output_description": "output_description0", "test_case_id": "1", "source": "source1", "samples": [{"input": "1 1", "output": "2"}], @@ -43,8 +43,8 @@ class ProblemAdminTest(APITestCase): ProblemTag.objects.create(name="tag2") self.problem = Problem.objects.create(title="title1", description="description1", - description_input="description_input1", - description_output="description_output1", + input_description="input1_description", + output_description="output1_description", test_case_id="1", source="source1", samples=[{"input": "1 1", "output": "2"}], @@ -63,8 +63,8 @@ class ProblemAdminTest(APITestCase): def test_release_problem_successfully(self): data = {"title": "title2", "description": "description2", - "description_input": "description_input2", - "description_output": "description_output2", + "input_description": "input_description2", + "output_description": "output_description2", "test_case_id": "1", "source": "source1", "samples": [{"input": "1 1", "output": "2"}], diff --git a/problem/views.py b/problem/views.py index 73fc81a..0f618cd 100644 --- a/problem/views.py +++ b/problem/views.py @@ -74,8 +74,8 @@ class ProblemAdminAPIView(APIView): data = serializer.data problem = Problem.objects.create(title=data["title"], description=data["description"], - description_input=data["description_input"], - description_output=data["description_output"], + input_description=data["input_description"], + output_description=data["output_description"], test_case_id=data["test_case_id"], source=data["source"], samples=json.dumps(data["samples"]), @@ -112,8 +112,8 @@ class ProblemAdminAPIView(APIView): problem.title = data["title"] problem.description = data["description"] - problem.description_input = data["description_input"] - problem.description_output = data["description_output"] + problem.input_description = data["input_description"] + problem.output_description = data["output_description"] problem.test_case_id = data["test_case_id"] problem.source = data["source"] problem.time_limit = data["time_limit"] From da03f902c5d96207602dfb1e47d550c10de0f338 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 13 Aug 2015 18:15:00 +0800 Subject: [PATCH 142/304] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=88=A4=E9=A2=98?= =?UTF-8?q?=E7=AB=AF=E7=9A=84=E9=83=A8=E5=88=86=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- judger/client.py | 2 +- judger/run.py | 37 ++++++++++++++++++++++++++++++------- judger_controller/tasks.py | 4 ++-- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/judger/client.py b/judger/client.py index 52f6586..fc7d0b9 100644 --- a/judger/client.py +++ b/judger/client.py @@ -58,7 +58,7 @@ class JudgeClient(object): # todo 系统调用白名单 chroot等参数 command = "lrun" + \ " --max-cpu-time " + str(self._max_cpu_time / 1000.0) + \ - " --max-real-time " + str(self._max_real_time / 1000.0) + \ + " --max-real-time " + str(self._max_real_time / 1000.0 * 2) + \ " --max-memory " + str(self._max_memory * 1000 * 1000) + \ " --network false" + \ " --uid " + str(lrun_uid) + \ diff --git a/judger/run.py b/judger/run.py index 1e83bb8..bdf037b 100644 --- a/judger/run.py +++ b/judger/run.py @@ -15,7 +15,7 @@ from settings import judger_workspace, mongodb_config # 参数有 -solution_id -time_limit -memory_limit -test_case_id # 获取到的值是['xxx.py', '-solution_id', '1111', '-time_limit', '1000', '-memory_limit', '100', '-test_case_id', 'aaaa'] args = sys.argv -solution_id = args[2] +submission_id = args[2] time_limit = args[4] memory_limit = args[6] test_case_id = args[8] @@ -23,33 +23,56 @@ test_case_id = args[8] connection = pymongo.MongoClient(host=mongodb_config["host"], port=mongodb_config["port"]) collection = connection["oj"]["oj_submission"] -submission = collection.find_one({"_id": ObjectId(solution_id)}) +submission = collection.find_one({"_id": ObjectId(submission_id)}) if not submission: exit() +connection.close() # 将代码写入文件 language = languages[submission["language"]] src_path = judger_workspace + "run/" + language["src_name"] f = open(src_path, "w") -f.write(submission["code"]) +f.write(submission["code"].encode("utf8")) f.close() # 编译 try: exe_path = compile_(language, src_path, judger_workspace + "run/") except Exception as e: - print {"result": result["compile_error"]} + print e + connection = pymongo.MongoClient(host=mongodb_config["host"], port=mongodb_config["port"]) + collection = connection["oj"]["oj_submission"] + data = {"result": result["compile_error"], "info": str(e)} + collection.find_one_and_update({"_id": ObjectId(submission_id)}, {"$set": data}) + connection.close() exit() + +print "Compile successfully" +# 运行 try: client = JudgeClient(language_code=submission["language"], exe_path=exe_path, max_cpu_time=int(time_limit), max_real_time=int(time_limit) * 2, max_memory=int(memory_limit), - test_case_dir= judger_workspace + "test_case/" + test_case_id + "/") - print client.run() + test_case_dir=judger_workspace + "test_case/" + test_case_id + "/") + judge_result = {"result": result["accepted"], "info": client.run()} + + for item in judge_result["info"]: + if item["result"]: + judge_result["result"] = item["result"] + break + except Exception as e: print e - print {"result": result["system_error"]} + judge_result = {"result": result["system_error"], "info": str(e)} + +print "Run successfully" +print judge_result +connection = pymongo.MongoClient(host=mongodb_config["host"], port=mongodb_config["port"]) +collection = connection["oj"]["oj_submission"] +collection.find_one_and_update({"_id": ObjectId(submission_id)}, {"$set": judge_result}) +connection.close() + diff --git a/judger_controller/tasks.py b/judger_controller/tasks.py index 054452c..ebc7b04 100644 --- a/judger_controller/tasks.py +++ b/judger_controller/tasks.py @@ -14,7 +14,7 @@ def judge(solution_id, time_limit, memory_limit, test_case_id): "python judger/run.py " "--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % (solution_id, str(time_limit), str(memory_limit), test_case_id), - # 如果设置的最长运行时间小于1000毫秒,那么/1000就是0,处理一下这个情况,设置为两秒 - timeout=(time_limit / 1000 * 3) or 2, shell=True) + # 设置最长运行时间是3倍的 cpu 时间 + timeout=(time_limit / 1000.0 * 3), shell=True) except subprocess.TimeoutExpired: print "docker timeout" From 5a967789a60cca70b096c1757f9a6d9ce58fa737 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 13 Aug 2015 18:15:54 +0800 Subject: [PATCH 143/304] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81=E7=9A=84=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/oj/problem/problem.js | 120 +++++++++++++++++++++--- submission/views.py | 6 +- template/oj/problem/problem.html | 53 ++++------- 3 files changed, 128 insertions(+), 51 deletions(-) diff --git a/static/src/js/app/oj/problem/problem.js b/static/src/js/app/oj/problem/problem.js index fd544cf..6826e7b 100644 --- a/static/src/js/app/oj/problem/problem.js +++ b/static/src/js/app/oj/problem/problem.js @@ -1,13 +1,19 @@ -require(["jquery", "code_mirror", "csrf"], function ($, code_mirror, csrfHeader) { +require(["jquery", "code_mirror", "csrf", "bs_alert"], function ($, code_mirror, csrfHeader, bs_alert) { var code_editor = code_mirror($("#code-editor")[0], "text/x-csrc"); - var language = "1"; + var language = $("input[name='language'][checked]").val(); + var submission_id; - $("#language-selector").change(function () { - language = $("#language-selector").val(); - var language_types = {c: "text/x-csrc", cpp: "text/x-c++src", java: "text/x-java"}; + $("input[name='language']").change(function () { + language = this.value; + var language_types = {"1": "text/x-csrc", "2": "text/x-c++src", "3": "text/x-java"}; code_editor.setOption("mode", language_types[language]); }); + $("#show-more-btn").click(function () { + $(".hide").attr("class", "problem-section"); + $("#show-more-btn").hide(); + }); + function show_loading() { $("#submit-code-button").attr("disabled", "disabled"); $("#loading-gif").show(); @@ -18,24 +24,108 @@ require(["jquery", "code_mirror", "csrf"], function ($, code_mirror, csrfHeader) $("#loading-gif").hide(); } + + function get_result_html(result) { + console.log(result); + // 0 结果正确 1 运行错误 2 超时 3 超内存 4 编译错误 + // 5 格式错误 6 结果错误 7 系统错误 8 等待判题 + var results = { + 0: {"alert_class": "success", message: "Accepted"}, + 1: {"alert_class": "danger", message: "Runtime Error"}, + 2: {"alert_class": "warning", message: "Time Limit Exceeded"}, + 3: {"alert_class": "warning", message: "Memory Limit Exceeded"}, + 4: {"alert_class": "danger", message: "Compile Error"}, + 5: {"alert_class": "warning", message: "Format Error"}, + 6: {"alert_class": "danger", message: "Wrong Answer"}, + 7: {"alert_class": "danger", message: "System Error"}, + 8: {"alert_class": "info", message: "Waiting"} + }; + + var html = ''; + console.log(html); + return html; + } + + function get_result() { + $.ajax({ + url: "/api/submission/?submission_id=" + submission_id, + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + // 8是还没有完成判题 + if (data.data.result == 8) { + // 1秒之后重新去获取 + setTimeout(get_result, 1000); + } + else { + hide_loading(); + $("#result").html(get_result_html(data.data.result)); + } + } + else { + bs_alert(data.data); + hide_loading(); + } + } + }) + } + $("#submit-code-button").click(function () { + var problem_id = window.location.pathname.split("/")[2]; + var code = code_editor.getValue(); + show_loading(); + + if(!code){ + bs_alert("请填写代码!"); + hide_loading(); + return false; + } + + $("#result").html(""); + $.ajax({ beforeSend: csrfHeader, url: "/api/submission/", method: "post", - data: JSON.stringify({problem_id: 2, language: language, code: code_editor.getValue()}), - contentType: "application/json" + data: JSON.stringify({ + problem_id: window.location.pathname.split("/")[2], + language: language, + code: code_editor.getValue() + }), + contentType: "application/json", + success: function (data) { + if (!data.code) { + submission_id = data.data.submission_id; + // 获取到id 之后2秒去查询一下判题结果 + setTimeout(get_result, 2000); + } + else { + bs_alert(data.data); + hide_loading(); + } + } }); - setTimeout( - function () { - $("#a").animate({opacity: '1'}) - }, 3); + }); - $("#show-more-btn").click(function(){ - $(".hide").attr("class", "problem-section"); - $("#show-more-btn").hide(); + $.ajax({ + url : "/api/user/", + method: "get", + dataType: "json", + success: function(data){ + if(data.code){ + $("#submit-code-button").attr("disabled", "disabled"); + $("#result").html(''); + } + } }) - }); diff --git a/submission/views.py b/submission/views.py index 7d2ef23..83cfb88 100644 --- a/submission/views.py +++ b/submission/views.py @@ -22,7 +22,7 @@ class SubmissionnAPIView(APIView): connection = pymongo.MongoClient(host=mongodb_setting["HOST"], port=mongodb_setting["PORT"]) return connection["oj"]["oj_submission"] - # @login_required + @login_required def post(self, request): """ 提交代码 @@ -48,12 +48,12 @@ class SubmissionnAPIView(APIView): else: return serializer_invalid_response(serializer) - # @login_required + @login_required def get(self, request): submission_id = request.GET.get("submission_id", None) if not submission_id: return error_response(u"参数错误") - submission = self._create_mondodb_connection().find_one({"_id": ObjectId(submission_id), "user_id": result.user.id}) + submission = self._create_mondodb_connection().find_one({"_id": ObjectId(submission_id), "user_id": request.user.id}) if submission: return success_response({"result": submission["result"]}) else: diff --git a/template/oj/problem/problem.html b/template/oj/problem/problem.html index b3f1623..f3a3021 100644 --- a/template/oj/problem/problem.html +++ b/template/oj/problem/problem.html @@ -9,7 +9,8 @@

{{ problem.title }}

-

发布时间: {{ problem.create_time }} CPU: {{ problem.time_limit }}ms 内存: {{ problem.memory_limit }}M

+

发布时间: {{ problem.create_time }} CPU: {{ problem.time_limit }}ms + 内存: {{ problem.memory_limit }}M

@@ -28,35 +29,35 @@

第一行包括两个数n,k

{% for item in samples %} -
- +
+
 {{ item.input }}
-
-
+
+
- +
 {{ item.output }}
-
+
{% endfor %}
{% if problem.hind %} -
- +
+ -

{{ problem.hint|safe }}

-
- {% endif %} +

{{ problem.hint|safe }}

+
+ {% endif %}

{% for tag in problem.tags.all %} - {{ tag.name }} + {{ tag.name }} {% endfor %}

@@ -66,13 +67,13 @@
@@ -89,24 +90,10 @@
-
-
- - - - + +
+
{% endblock %} From 0aeeab3c8c170345e0d0cf8f6af5543e99da8fc7 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 13 Aug 2015 20:28:23 +0800 Subject: [PATCH 144/304] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9=E6=9C=80?= =?UTF-8?q?=E5=A4=A7=20cpu=20=E6=97=B6=E9=97=B4=E7=9A=84=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- judger/run.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/judger/run.py b/judger/run.py index bdf037b..d14fc61 100644 --- a/judger/run.py +++ b/judger/run.py @@ -62,6 +62,9 @@ try: if item["result"]: judge_result["result"] = item["result"] break + else: + l = sorted(judge_result["info"], key=lambda k: k["cpu_time"]) + judge_result["accepted_answer_info"] = {"time": l[-1]["cpu_time"]} except Exception as e: print e From 2731bc92490bb0e68e7b867758851072185f88c8 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 13 Aug 2015 20:28:41 +0800 Subject: [PATCH 145/304] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20docker=20=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E6=97=B6=E9=97=B4=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- judger_controller/tasks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/judger_controller/tasks.py b/judger_controller/tasks.py index ebc7b04..3f4e255 100644 --- a/judger_controller/tasks.py +++ b/judger_controller/tasks.py @@ -14,7 +14,7 @@ def judge(solution_id, time_limit, memory_limit, test_case_id): "python judger/run.py " "--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % (solution_id, str(time_limit), str(memory_limit), test_case_id), - # 设置最长运行时间是3倍的 cpu 时间 - timeout=(time_limit / 1000.0 * 3), shell=True) + # 设置最长运行时间是5倍的 cpu 时间 + timeout=(time_limit / 1000.0 * 5), shell=True) except subprocess.TimeoutExpired: print "docker timeout" From 58f90146889491c15459494ef8db25ef13ccb0a3 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 13 Aug 2015 20:29:11 +0800 Subject: [PATCH 146/304] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=A2=98=E7=9B=AE?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 6 +++--- problem/migrations/0006_merge.py | 15 ++++++++++++++ problem/views.py | 11 +---------- static/src/js/app/oj/problem/problem.js | 21 ++++++++++---------- submission/views.py | 26 +++++++++++++++++++++---- template/oj/problem/problem.html | 6 +++--- 6 files changed, 55 insertions(+), 30 deletions(-) create mode 100644 problem/migrations/0006_merge.py diff --git a/oj/urls.py b/oj/urls.py index 3165c1a..67755df 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -50,9 +50,9 @@ urlpatterns = [ url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="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_solutions/', "problem.views.problem_my_solutions_list_page", - name="problem_my_solutions_page"), - url(r'^my_solution/(?P\d+)/$', "problem.views.my_solution", name="my_solution_page"), + url(r'^problem/(?P\d+)/my_solutions/', "submission.views.problem_my_submissions_list_page", + name="problem_my_submissions_page"), + url(r'^my_solution/(?P\d+)/$', "submission.views.my_submission", name="my_submission_page"), url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), name="join_group_request_admin_api"), diff --git a/problem/migrations/0006_merge.py b/problem/migrations/0006_merge.py new file mode 100644 index 0000000..f24e12d --- /dev/null +++ b/problem/migrations/0006_merge.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('problem', '0005_auto_20150813_1807'), + ('problem', '0004_auto_20150812_2254'), + ] + + operations = [ + ] diff --git a/problem/views.py b/problem/views.py index fe20127..a65aa4c 100644 --- a/problem/views.py +++ b/problem/views.py @@ -47,15 +47,6 @@ def problem_page(request, problem_id): return render(request, "oj/problem/problem.html", {"problem": problem, "samples": json.loads(problem.samples)}) -def problem_my_solutions_list_page(request, problem_id): - return render(request, "oj/problem/my_solutions_list.html") - - -def my_solution(request, solution_id): - return render(request, "oj/problem/my_solution.html") - - - class ProblemAdminAPIView(APIView): def post(self, request): """ @@ -197,7 +188,7 @@ class TestCaseUploadAPIView(APIView): os.mkdir(test_case_dir) for name in l: f = open(test_case_dir + name, "wb") - f.write(test_case_file.read(name)) + f.write(test_case_file.read(name).replace("\r\n", "\n")) f.close() l.sort() diff --git a/static/src/js/app/oj/problem/problem.js b/static/src/js/app/oj/problem/problem.js index 6826e7b..141405a 100644 --- a/static/src/js/app/oj/problem/problem.js +++ b/static/src/js/app/oj/problem/problem.js @@ -25,8 +25,7 @@ require(["jquery", "code_mirror", "csrf", "bs_alert"], function ($, code_mirror, } - function get_result_html(result) { - console.log(result); + function get_result_html(data) { // 0 结果正确 1 运行错误 2 超时 3 超内存 4 编译错误 // 5 格式错误 6 结果错误 7 系统错误 8 等待判题 var results = { @@ -42,14 +41,16 @@ require(["jquery", "code_mirror", "csrf", "bs_alert"], function ($, code_mirror, }; var html = ''; - console.log(html); + results[data.result].message + + '!   '; + if (!data.result) { + html += "CPU time: " + data.accepted_answer_info.time + "ms   "; + } + html += ('查看详情
'); + return html; } @@ -67,7 +68,7 @@ require(["jquery", "code_mirror", "csrf", "bs_alert"], function ($, code_mirror, } else { hide_loading(); - $("#result").html(get_result_html(data.data.result)); + $("#result").html(get_result_html(data.data)); } } else { @@ -84,7 +85,7 @@ require(["jquery", "code_mirror", "csrf", "bs_alert"], function ($, code_mirror, show_loading(); - if(!code){ + if(!code.trim()){ bs_alert("请填写代码!"); hide_loading(); return false; diff --git a/submission/views.py b/submission/views.py index 83cfb88..3d115a0 100644 --- a/submission/views.py +++ b/submission/views.py @@ -1,4 +1,5 @@ # coding=utf-8 +import datetime import pymongo from bson.objectid import ObjectId @@ -16,12 +17,13 @@ from utils.shortcuts import serializer_invalid_response, error_response, success from .serializers import CreateSubmissionSerializer -class SubmissionnAPIView(APIView): - def _create_mondodb_connection(self): +def _create_mondodb_connection(): mongodb_setting = settings.DATABASES["mongodb"] connection = pymongo.MongoClient(host=mongodb_setting["HOST"], port=mongodb_setting["PORT"]) return connection["oj"]["oj_submission"] + +class SubmissionnAPIView(APIView): @login_required def post(self, request): """ @@ -35,6 +37,7 @@ class SubmissionnAPIView(APIView): # data["language"] = int(data["language"]) data["user_id"] = request.user.id data["result"] = result["waiting"] + data["create_time"] = datetime.datetime.now() try: problem = Problem.objects.get(id=data["problem_id"]) except Problem.DoesNotExist: @@ -53,8 +56,23 @@ class SubmissionnAPIView(APIView): submission_id = request.GET.get("submission_id", None) if not submission_id: return error_response(u"参数错误") - submission = self._create_mondodb_connection().find_one({"_id": ObjectId(submission_id), "user_id": request.user.id}) + submission = _create_mondodb_connection().find_one({"_id": ObjectId(submission_id), "user_id": request.user.id}) if submission: - return success_response({"result": submission["result"]}) + response_data = {"result": submission["result"]} + if submission["result"] == 0: + response_data["accepted_answer_info"] = submission["accepted_answer_info"] + return success_response(response_data) else: return error_response(u"提交不存在") + + +def problem_my_submissions_list_page(request, problem_id): + collection = _create_mondodb_connection() + submissions = collection.find({"problem_id": int(problem_id), "user_id": request.user.id}, + projection=["result", "accepted_answer_info", "create_time"], + sort=[["create_time", -pymongo.ASCENDING]]) + return render(request, "oj/problem/my_solutions_list.html", {"submissions": submissions}) + + +def my_submission(request, solution_id): + return render(request, "oj/problem/my_solution.html") \ No newline at end of file diff --git a/template/oj/problem/problem.html b/template/oj/problem/problem.html index f3a3021..bcb6160 100644 --- a/template/oj/problem/problem.html +++ b/template/oj/problem/problem.html @@ -21,12 +21,12 @@
-

第一行包括两个数n,k

+

{{ problem.input_description }}

-

第一行包括两个数n,k

+

{{ problem.output_description }}k

{% for item in samples %}
@@ -45,7 +45,7 @@
- {% if problem.hind %} + {% if problem.hint %}
From 94282f3ed859fc0ffc3fde830fe3c22d203ae0aa Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 13 Aug 2015 21:02:17 +0800 Subject: [PATCH 147/304] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=88=91=E7=9A=84?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E5=88=97=E8=A1=A8=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/settings.py | 1 + oj/urls.py | 6 +- submission/templatetags/__init__.py | 1 + submission/templatetags/submission.py | 28 +++++++++ submission/views.py | 4 +- template/oj/problem/my_solutions_list.html | 53 ---------------- template/oj/problem/my_submissions_list.html | 64 ++++++++++++++++++++ template/oj/problem/problem.html | 4 +- 8 files changed, 101 insertions(+), 60 deletions(-) create mode 100644 submission/templatetags/__init__.py create mode 100644 submission/templatetags/submission.py delete mode 100644 template/oj/problem/my_solutions_list.html create mode 100644 template/oj/problem/my_submissions_list.html diff --git a/oj/settings.py b/oj/settings.py index 01bbadc..8d012d0 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -51,6 +51,7 @@ INSTALLED_APPS = ( 'group', 'problem', 'admin', + 'submission', 'rest_framework', 'rest_framework_swagger', diff --git a/oj/urls.py b/oj/urls.py index 67755df..78791d5 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -42,7 +42,7 @@ urlpatterns = [ 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(), + 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/group_member/$', GroupMemberAdminAPIView.as_view(), name="group_member_admin_api"), @@ -50,9 +50,9 @@ urlpatterns = [ url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="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_solutions/', "submission.views.problem_my_submissions_list_page", + url(r'^problem/(?P\d+)/my_solutions/$', "submission.views.problem_my_submissions_list_page", name="problem_my_submissions_page"), - url(r'^my_solution/(?P\d+)/$', "submission.views.my_submission", name="my_submission_page"), + url(r'^my_solution/(?P\w+)/$', "submission.views.my_submission", name="my_submission_page"), url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), name="join_group_request_admin_api"), diff --git a/submission/templatetags/__init__.py b/submission/templatetags/__init__.py new file mode 100644 index 0000000..9bad579 --- /dev/null +++ b/submission/templatetags/__init__.py @@ -0,0 +1 @@ +# coding=utf-8 diff --git a/submission/templatetags/submission.py b/submission/templatetags/submission.py new file mode 100644 index 0000000..51806f0 --- /dev/null +++ b/submission/templatetags/submission.py @@ -0,0 +1,28 @@ +# coding=utf-8 + + +def translate_result(value): + print value + results = { + 0: "Accepted", + 1: "Runtime Error", + 2: "Time Limit Exceeded", + 3: "Memory Limit Exceeded", + 4: "Compile Error", + 5: "Format Error", + 6: "Wrong Answer", + 7: "System Error", + 8: "Waiting" + } + return results[value] + + +def translate_id(submission_item): + print submission_item + return submission_item["_id"] + +from django import template + +register = template.Library() +register.filter("translate_result", translate_result) +register.filter("translate_id", translate_id) \ No newline at end of file diff --git a/submission/views.py b/submission/views.py index 3d115a0..72a1f4a 100644 --- a/submission/views.py +++ b/submission/views.py @@ -69,9 +69,9 @@ class SubmissionnAPIView(APIView): def problem_my_submissions_list_page(request, problem_id): collection = _create_mondodb_connection() submissions = collection.find({"problem_id": int(problem_id), "user_id": request.user.id}, - projection=["result", "accepted_answer_info", "create_time"], + projection=["result", "accepted_answer_info", "create_time", "language"], sort=[["create_time", -pymongo.ASCENDING]]) - return render(request, "oj/problem/my_solutions_list.html", {"submissions": submissions}) + return render(request, "oj/problem/my_submissions_list.html", {"submissions": submissions}) def my_submission(request, solution_id): diff --git a/template/oj/problem/my_solutions_list.html b/template/oj/problem/my_solutions_list.html deleted file mode 100644 index 613410a..0000000 --- a/template/oj/problem/my_solutions_list.html +++ /dev/null @@ -1,53 +0,0 @@ -{% extends 'oj_base.html' %} - -{% block body %} - -
- -

Battle Over Cities - Hard Version

-

cpu: 1000ms 内存: 256M

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#提交时间结果运行时间运行内存语言
11Error Format333
2WrongWrong Answer@fat33
3LarryAccepted@twitter33
-
-{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/my_submissions_list.html b/template/oj/problem/my_submissions_list.html new file mode 100644 index 0000000..cc9e138 --- /dev/null +++ b/template/oj/problem/my_submissions_list.html @@ -0,0 +1,64 @@ +{% extends 'oj_base.html' %} + +{% block body %} + + {% load submission %} +
+ +

Battle Over Cities - Hard Version

+ +

cpu: 1000ms 内存: 256M

+ + + + + + + + + + + + {% for item in submissions %} + {% ifequal item.result 0 %} + + {% else %} + {% ifequal item.result 8 %} + + {% else %} + + {% endifequal %} + + {% endifequal %} + + + + + + + {% endfor %} + + +
#提交时间结果运行时间语言
{{ forloop.counter }}{{ item.create_time }}{{ item.result|translate_result }} + {% if item.accepted_answer_info.time %} + {{ item.accepted_answer_info.time }}ms + {% else %} + -- + {% endif %} + + {% ifequal item.language 1 %} + C + {% else %} + {% ifequal item.language 2 %} + C++ + {% else %} + Java + {% endifequal %} + {% endifequal %} +
+
+{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/problem.html b/template/oj/problem/problem.html index bcb6160..1764db2 100644 --- a/template/oj/problem/problem.html +++ b/template/oj/problem/problem.html @@ -67,10 +67,10 @@
'); + html += ('查看详情
'); return html; } diff --git a/static/src/js/utils/bs_alert.js b/static/src/js/utils/bs_alert.js index b35f06d..547b919 100644 --- a/static/src/js/utils/bs_alert.js +++ b/static/src/js/utils/bs_alert.js @@ -1,7 +1,17 @@ define("bs_alert", ["jquery", "bootstrap"], function($){ function bs_alert(content){ + if(!$("#alert-modal").length) { + var html = ''; + $("body").append(html); + } $("#modal-text").html(content); - $("#modal").modal(); + $("#alert-modal").modal(); } return bs_alert; }); \ No newline at end of file diff --git a/submission/views.py b/submission/views.py index 72a1f4a..0a923e0 100644 --- a/submission/views.py +++ b/submission/views.py @@ -13,12 +13,12 @@ from judger.result import result from judger_controller.tasks import judge from account.decorators import login_required from problem.models import Problem -from utils.shortcuts import serializer_invalid_response, error_response, success_response +from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page from .serializers import CreateSubmissionSerializer def _create_mondodb_connection(): - mongodb_setting = settings.DATABASES["mongodb"] + mongodb_setting = settings["mongodb_setting"] connection = pymongo.MongoClient(host=mongodb_setting["HOST"], port=mongodb_setting["PORT"]) return connection["oj"]["oj_submission"] @@ -66,13 +66,31 @@ class SubmissionnAPIView(APIView): return error_response(u"提交不存在") +@login_required def problem_my_submissions_list_page(request, problem_id): collection = _create_mondodb_connection() submissions = collection.find({"problem_id": int(problem_id), "user_id": request.user.id}, projection=["result", "accepted_answer_info", "create_time", "language"], sort=[["create_time", -pymongo.ASCENDING]]) - return render(request, "oj/problem/my_submissions_list.html", {"submissions": submissions}) + try: + problem = Problem.objects.get(id=problem_id, visible=True) + except Problem.DoesNotExist: + return error_page(request, u"问题不存在") + return render(request, "oj/problem/my_submissions_list.html", + {"submissions": submissions, "problem": problem}) -def my_submission(request, solution_id): - return render(request, "oj/problem/my_solution.html") \ No newline at end of file +@login_required +def my_submission(request, submission_id): + collection = _create_mondodb_connection() + submission = collection.find_one({"user_id": request.user.id, "_id": ObjectId(submission_id)}, + projection=["result", "accepted_answer_info", "create_time", + "language", "code", "problem_id", "info"]) + if not submission: + return error_page(request, u"提交不存在") + try: + problem = Problem.objects.get(id=submission["problem_id"], visible=True) + except Problem.DoesNotExist: + return error_page(request, u"提交不存在") + + return render(request, "oj/problem/my_submission.html", {"submission": submission, "problem": problem}) \ No newline at end of file diff --git a/template/admin/admin.html b/template/admin/admin.html index 69aba41..d5a0e18 100644 --- a/template/admin/admin.html +++ b/template/admin/admin.html @@ -124,23 +124,7 @@
- + diff --git a/template/admin_base.html b/template/admin_base.html deleted file mode 100644 index 86cf7e2..0000000 --- a/template/admin_base.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - 在线评测系统 - 后台管理 - - - {% block css_block %}{% endblock %} - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - - - {% block body %}{% endblock %} - -
-
- - - - - - - -{% block js_block %}{% endblock %} - - - - - \ No newline at end of file diff --git a/template/oj/problem/_problem_header.html b/template/oj/problem/_problem_header.html new file mode 100644 index 0000000..82d118f --- /dev/null +++ b/template/oj/problem/_problem_header.html @@ -0,0 +1,7 @@ + +

{{ problem.title }}

+ +

发布时间 : {{ problem.create_time }}   + 时间限制 : {{ problem.time_limit }}ms   + 内存限制 : {{ problem.memory_limit }}M +

\ No newline at end of file diff --git a/template/oj/problem/my_submission.html b/template/oj/problem/my_submission.html new file mode 100644 index 0000000..e7f6ce4 --- /dev/null +++ b/template/oj/problem/my_submission.html @@ -0,0 +1,57 @@ +{% extends 'oj_base.html' %} +{% block css_block %} + +{% endblock %} +{% block body %} + {% load submission %} +
+ + {% include "oj/problem/_problem_header.html" %} +
+
+

运行结果 : + {{ submission.result|translate_result }} + +

+ {% ifequal submission.result 0 %} +

时间 : {{ submission.accepted_answer_info.time }}ms 语言 : + {{ submission.language|translate_language }} +

+ {% endifequal %} + {% ifequal submission.result 4 %} +

{{ submission.info }}

+ {% endifequal %} +

提交时间 : {{ submission.create_time }}

+
+
+
+ +
+
+ +{% endblock %} +{% block js_block %} + +{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/my_submissions_list.html b/template/oj/problem/my_submissions_list.html index cc9e138..b1c6388 100644 --- a/template/oj/problem/my_submissions_list.html +++ b/template/oj/problem/my_submissions_list.html @@ -3,62 +3,48 @@ {% block body %} {% load submission %} -
- -

Battle Over Cities - Hard Version

+
+ +

{{ problem.title }}

-

cpu: 1000ms 内存: 256M

- - - - - - - - - - - - {% for item in submissions %} - {% ifequal item.result 0 %} - - {% else %} - {% ifequal item.result 8 %} - +

发布时间: {{ problem.create_time }}   + 时间限制: {{ problem.time_limit }}ms   + 内存限制: {{ problem.memory_limit }}M

+
#提交时间结果运行时间语言
+ + + + + + + + + + + {% for item in submissions %} + + + + + - {% endifequal %} + -- + {% endif %} + + + + {% endfor %} - {% endifequal %} - - - - - - - {% endfor %} - - -
#提交时间结果运行时间语言
{{ forloop.counter }}{{ item.create_time }}{{ item.result|translate_result }} + {% if item.accepted_answer_info.time %} + {{ item.accepted_answer_info.time }}ms {% else %} -
+ {{ item.language|translate_language }} +
{{ forloop.counter }}{{ item.create_time }}{{ item.result|translate_result }} - {% if item.accepted_answer_info.time %} - {{ item.accepted_answer_info.time }}ms - {% else %} - -- - {% endif %} - - {% ifequal item.language 1 %} - C - {% else %} - {% ifequal item.language 2 %} - C++ - {% else %} - Java - {% endifequal %} - {% endifequal %} -
-
+ + +
{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/problem.html b/template/oj/problem/problem.html index 1764db2..2d8ddbb 100644 --- a/template/oj/problem/problem.html +++ b/template/oj/problem/problem.html @@ -5,12 +5,9 @@ -

{{ problem.title }}

- -

发布时间: {{ problem.create_time }} CPU: {{ problem.time_limit }}ms - 内存: {{ problem.memory_limit }}M

+ {% include "oj/problem/_problem_header.html" %}
@@ -93,7 +90,7 @@
-
+
{% endblock %} diff --git a/template/oj_base.html b/template/oj_base.html index 0cfcf82..4a17157 100644 --- a/template/oj_base.html +++ b/template/oj_base.html @@ -8,13 +8,13 @@ 在线评测系统 - - {% block css_block %}{% endblock %} - - + + + {% block css_block %}{% endblock %} + @@ -47,7 +47,7 @@
  • 比赛
  • 关于
  • - {% if not request.user.is_authenticated %} + {% if request.user.is_authenticated %}
    '); + html += ('查看详情
    '); return html; } - function get_result() { + function getResult() { $.ajax({ - url: "/api/submission/?submission_id=" + submission_id, + url: "/api/submission/?submission_id=" + submissionId, method: "get", dataType: "json", success: function (data) { @@ -64,54 +63,54 @@ require(["jquery", "code_mirror", "csrf", "bs_alert"], function ($, code_mirror, // 8是还没有完成判题 if (data.data.result == 8) { // 1秒之后重新去获取 - setTimeout(get_result, 1000); + setTimeout(getResult, 1000); } else { - hide_loading(); - $("#result").html(get_result_html(data.data)); + hideLoading(); + $("#result").html(getResultHtml(data.data)); } } else { - bs_alert(data.data); - hide_loading(); + bsAlert(data.data); + hideLoading(); } } }) } $("#submit-code-button").click(function () { - var problem_id = window.location.pathname.split("/")[2]; - var code = code_editor.getValue(); + var problemId = window.location.pathname.split("/")[2]; + var code = codeEditor.getValue(); - show_loading(); + showLoading(); if(!code.trim()){ bs_alert("请填写代码!"); - hide_loading(); + hideLoading(); return false; } $("#result").html(""); $.ajax({ - beforeSend: csrfHeader, + beforeSend: csrfTokenHeader, url: "/api/submission/", method: "post", data: JSON.stringify({ - problem_id: window.location.pathname.split("/")[2], + problem_id: problemId, language: language, - code: code_editor.getValue() + code: codeEditor.getValue() }), contentType: "application/json", success: function (data) { if (!data.code) { - submission_id = data.data.submission_id; + submissionId = data.data.submission_id; // 获取到id 之后2秒去查询一下判题结果 - setTimeout(get_result, 2000); + setTimeout(getResult, 2000); } else { bs_alert(data.data); - hide_loading(); + hideLoading(); } } }); diff --git a/static/src/js/config.js b/static/src/js/config.js index 0c63006..d74fcdd 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -2,24 +2,28 @@ var require = { // RequireJS 通过一个相对的路径 baseUrl来加载所有代码。baseUrl通常被设置成data-main属性指定脚本的同级目录。 baseUrl: "/static/js/", paths: { - //百度webuploader - webuploader: "lib/webuploader/webuploader", jquery: "lib/jquery/jquery", avalon: "lib/avalon/avalon", editor: "utils/editor", uploader: "utils/uploader", - validation: "utils/validation", - code_mirror: "utils/code_mirror", - bs_alert: "utils/bs_alert", + formValidation: "utils/formValidation", + codeMirror: "utils/codeMirror", + bsAlert: "utils/bsAlert", problem: "app/oj/problem/problem", contest: "app/admin/contest/contest", - csrf: "utils/csrf", + csrfToken: "utils/csrfToken", admin: "app/admin/admin", chart: "lib/chart/Chart", tagEditor: "lib/tagEditor/jquery.tag-editor.min", jqueryUI: "lib/jqueryUI/jquery-ui", - //formValidation 不要在代码中单独使用,而是使用和修改utils/validation + bootstrap: "lib/bootstrap/bootstrap", + datetimePicker: "lib/datetime_picker/bootstrap-datetimepicker.zh-CN", + + + // ------ 下面写的都不要直接用,而是使用上面的封装版本 ------ + + //formValidation -> utils/validation base: "lib/formValidation/base", helper: "lib/formValidation/helper", "language/zh_CN": "lib/formValidation/language/zh_CN", @@ -32,26 +36,25 @@ var require = { "validator/confirm":"lib/formValidation/validator/confirm", "validator/remote":"lib/formValidation/validator/remote", "validator/emailAddress":"lib/formValidation/validator/emailAddress", - //富文本编辑器 不要直接使用,而是使用上面的editor + + //富文本编辑器simditor -> editor simditor: "lib/simditor/simditor", "simple-module": "lib/simditor/module", "simple-hotkeys": "lib/simditor/hotkeys", "simple-uploader": "lib/simditor/uploader", - //code mirroe 代码编辑器 - _code_mirror: "lib/codeMirror/codemirror", - code_mirror_clang: "lib/codeMirror/language/clike", + //code mirror 代码编辑器 ->codeMirror + _codeMirror: "lib/codeMirror/codemirror", + codeMirrorClang: "lib/codeMirror/language/clike", - //bootstrap - bootstrap: "lib/bootstrap/bootstrap", + //百度webuploader -> uploader + webUploader: "lib/webuploader/webuploader", - // - "_datetimepicker": "lib/datetime_picker/bootstrap-datetimepicker", - "datetimepicker": "lib/datetime_picker/bootstrap-datetimepicker.zh-CN" + "_datetimePicker": "lib/datetime_picker/bootstrap-datetimepicker" }, shim: { - "bootstrap": {"deps": ['jquery']}, - "_datetimepicker": {"deps": ["jquery"]}, - "datetimepicker": {"deps": ["_datetimepicker"]} + bootstrap: {deps: ["jquery"]}, + _datetimePicker: {dep: ["jquery"]}, + datetimePicker: {deps: ["_datetimePicker"]} } }; \ No newline at end of file diff --git a/static/src/js/lib/codeMirror/language/clike.js b/static/src/js/lib/codeMirror/language/clike.js index 660b547..ef29ae9 100755 --- a/static/src/js/lib/codeMirror/language/clike.js +++ b/static/src/js/lib/codeMirror/language/clike.js @@ -5,7 +5,7 @@ if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD - define(["_code_mirror"], mod); + define(["_codeMirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { diff --git a/static/src/js/lib/formValidation/validator/remote.js b/static/src/js/lib/formValidation/validator/remote.js index 83a6fc5..0f48676 100755 --- a/static/src/js/lib/formValidation/validator/remote.js +++ b/static/src/js/lib/formValidation/validator/remote.js @@ -7,12 +7,12 @@ // AMD module is defined if (typeof define === "function" && define.amd) { - define("validator/remote", ["jquery", "base", "csrf"], factory); + define("validator/remote", ["jquery", "base", "csrfToken"], factory); } else { // planted over the root! factory(root.jQuery, root.FormValidation); } -}(this, function ($, FormValidation, csrfHeader) { +}(this, function ($, FormValidation, csrfTokenHeader) { FormValidation.I18n = $.extend(true, FormValidation.I18n || {}, { 'en_US': { remote: { @@ -28,7 +28,7 @@ return true; var url = options.url; var xhr = $.ajax({ - beforeSend: csrfHeader, + beforeSend: csrfTokenHeader, url: url, dataType: 'json', data: ajaxData, diff --git a/static/src/js/utils/bs_alert.js b/static/src/js/utils/bsAlert.js similarity index 88% rename from static/src/js/utils/bs_alert.js rename to static/src/js/utils/bsAlert.js index 547b919..52f46f8 100644 --- a/static/src/js/utils/bs_alert.js +++ b/static/src/js/utils/bsAlert.js @@ -1,5 +1,5 @@ -define("bs_alert", ["jquery", "bootstrap"], function($){ - function bs_alert(content){ +define("bsAlert", ["jquery", "bootstrap"], function($){ + function bsAlert(content){ if(!$("#alert-modal").length) { var html = '