Accept Merge Request #131 Alpha 1.0 : (dev -> master)

Merge Request: Alpha 1.0
Created By: @virusdefender
Accepted By: @virusdefender
URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/131
This commit is contained in:
virusdefender 2015-08-25 22:58:05 +08:00
commit c689888bbd
339 changed files with 20833 additions and 26774 deletions

7
.gitignore vendored
View File

@ -55,4 +55,9 @@ db.db
db.sqlite3
.DS_Store
log/
release/
static/release/css
static/release/js
static/release/img
build.txt
tmp/
test_case/

View File

@ -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

35
account/decorators.py Normal file
View File

@ -0,0 +1,35 @@
# coding=utf-8
from functools import wraps
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from utils.shortcuts import error_response, error_page
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 一个参数
request = args[-1]
if request.user.is_authenticated():
return func(*args, **kwargs)
if request.is_ajax():
return error_response(u"请先登录")
else:
return HttpResponseRedirect("/login/")
return check
def admin_required(func):
@wraps(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 error_page(request, u"需要管理员权限,如果没有登录,请先登录")
return check

View File

@ -2,7 +2,7 @@
from __future__ import unicode_literals
from django.db import models, migrations
import django.db.models.deletion
import account.models
class Migration(migrations.Migration):
@ -18,10 +18,17 @@ class Migration(migrations.Migration):
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(null=True, verbose_name='last login', blank=True)),
('username', models.CharField(unique=True, max_length=30)),
('real_name', models.CharField(max_length=30, null=True, blank=True)),
('email', models.EmailField(max_length=254, null=True, blank=True)),
('create_time', models.DateTimeField(auto_now_add=True)),
('admin_type', models.IntegerField(default=0)),
],
options={
'db_table': 'user',
},
managers=[
('objects', account.models.UserManager()),
],
),
migrations.CreateModel(
name='AdminGroup',
@ -29,9 +36,4 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
],
),
migrations.AddField(
model_name='user',
name='admin_group',
field=models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, to='account.AdminGroup', null=True),
),
]

View File

@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import account.models
class Migration(migrations.Migration):
dependencies = [
('account', '0001_initial'),
]
operations = [
migrations.AlterModelManagers(
name='user',
managers=[
(b'objects', account.models.UserManager()),
],
),
migrations.AddField(
model_name='user',
name='real_name',
field=models.CharField(max_length=30, null=True, blank=True),
),
]

View File

@ -14,12 +14,22 @@ class UserManager(models.Manager):
return self.get(**{self.model.USERNAME_FIELD: username})
REGULAR_USER = 0
ADMIN = 1
SUPER_ADMIN = 2
class User(AbstractBaseUser):
# 用户名
username = models.CharField(max_length=30, unique=True)
# 真实姓名
real_name = models.CharField(max_length=30, blank=True, null=True)
admin_group = models.ForeignKey(AdminGroup, null=True, on_delete=models.SET_NULL)
# 用户邮箱
email = models.EmailField(max_length=254, blank=True, null=True)
# 用户注册时间
create_time = models.DateTimeField(auto_now_add=True)
# 0代表不是管理员 1是普通管理员 2是超级管理员
admin_type = models.IntegerField(default=0)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []

View File

@ -1,6 +1,8 @@
# coding=utf-8
from rest_framework import serializers
from .models import User
class UserLoginSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30)
@ -11,14 +13,34 @@ 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):
username = serializers.CharField(max_length=30)
old_password = serializers.CharField(max_length=30, min_length=6)
old_password = serializers.CharField()
new_password = serializers.CharField(max_length=30, min_length=6)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
exclude = ["password"]
class EditUserSerializer(serializers.Serializer):
id = serializers.IntegerField()
username = serializers.CharField(max_length=30)
real_name = serializers.CharField(max_length=30)
password = serializers.CharField(max_length=30, min_length=6, required=False, default=None)
email = serializers.EmailField(max_length=254)
admin_type = serializers.IntegerField(default=0)

20
account/test_urls.py Normal file
View File

@ -0,0 +1,20 @@
# coding=utf-8
from django.conf.urls import include, url
from django.views.generic import TemplateView
from .tests import (LoginRequiredCBVTestWithArgs, LoginRequiredCBVTestWithoutArgs,
AdminRequiredCBVTestWithArgs, AdminRequiredCBVTestWithoutArgs)
urlpatterns = [
url(r'^login_required_test/fbv/1/$', "account.tests.login_required_FBV_test_without_args"),
url(r'^login_required_test/fbv/(?P<problem_id>\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<problem_id>\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<problem_id>\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<problem_id>\d+)/$', AdminRequiredCBVTestWithArgs.as_view()),
url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"),
]

View File

@ -1,10 +1,17 @@
# coding=utf-8
import json
from django.core.urlresolvers import reverse
from django.test import TestCase, Client
from django.http import HttpResponse
from django.contrib import auth
from rest_framework.test import APITestCase, APIClient
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import User
from .models import User, SUPER_ADMIN
from .decorators import login_required, admin_required
class UserLoginTest(TestCase):
@ -57,6 +64,25 @@ 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)
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()
@ -68,22 +94,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"}
@ -91,11 +130,242 @@ class UserChangePasswordAPITest(APITestCase):
self.assertEqual(response.data, {"code": 1, "data": u"密码不正确,请重新修改!"})
def test_invalid_data_format(self):
data = {"username": "test", "old_password": "aaa", "new_password": "aaaddd"}
data = {"old_password": "aaa", "new_password": "aaaddd"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 1)
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)
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"用户密码修改成功!"})
class UserAdminAPITest(APITestCase):
def setUp(self):
self.client = APIClient()
self.url = reverse("user_admin_api")
user = User.objects.create(username="testx", real_name="xx", admin_type=SUPER_ADMIN)
user.set_password("testxx")
user.save()
user = User.objects.create(username="testy", real_name="yy", admin_type=SUPER_ADMIN)
user.set_password("testyy")
user.save()
self.client.login(username="testx", password="testxx")
# 以下是编辑用户的测试
def test_success_get_data(self):
self.assertEqual(self.client.get(self.url).data["code"], 0)
def test_put_invalid_data(self):
data = {"username": "test", "password": "testaa", "email": "60@qq.com", "admin_type": "2"}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_user_does_not_exist(self):
data = {"id": 3, "username": "test0", "real_name": "test00",
"password": "testaa", "email": "60@qq.com", "admin_type": "2"}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"该用户不存在!"})
def test_username_exists(self):
data = {"id": 1, "username": "testy", "real_name": "test00",
"password": "testaa", "email": "60@qq.com", "admin_type": "2"}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"昵称已经存在"})
def test_user_edit_not_password_successfully(self):
data = {"id": 1, "username": "test0", "real_name": "test00",
"email": "60@qq.com", "admin_type": "2"}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0)
def test_user_edit_change_password_successfully(self):
data = {"id": 1, "username": "test0", "real_name": "test00", "password": "111111",
"email": "60@qq.com", "admin_type": "2"}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0)
self.assertIsNotNone(auth.authenticate(username="test0", password="111111"))
def test_error_admin_type(self):
response = self.client.get(self.url + "?admin_type=error")
self.assertEqual(response.data, {"code": 1, "data": u"参数错误"})
# 以下是用户分页的测试
def test_query_by_keyword(self):
user1 = User.objects.create(username="test1", real_name="aa")
user1.set_password("testaa")
user1.save()
user2 = User.objects.create(username="test2", real_name="bb")
user2.set_password("testbb")
user2.save()
user3 = User.objects.create(username="test3", real_name="cc")
user3.set_password("testcc")
user3.save()
response = self.client.get(self.url + "?keyword=test1")
self.assertEqual(response.data["code"], 0)
class UserInfoAPITest(APITestCase):
def setUp(self):
self.client = APIClient()
self.url = reverse('user_info_api')
user = User.objects.create(username="test1", real_name="aa")
user.set_password("testaa")
user.save()
def test_get_data_successfully(self):
self.client.login(username="test1", password="testaa")
self.assertEqual(self.client.get(self.url).data["code"], 0)
@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("/login_required_test/fbv/1/")
self.assertRedirects(response, "/login/")
# 登陆后
self.client.login(username="test", password="test")
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("/login_required_test/fbv/1024/")
self.assertRedirects(response, "/login/")
# 登陆后
self.client.login(username="test", password="test")
response = self.client.get("/login_required_test/fbv/1024/")
self.assertEqual(response.content, "1024")
def test_cbv_without_args(self):
# 没登陆
response = self.client.get("/login_required_test/cbv/1/")
self.assertRedirects(response, "/login/")
# 登陆后
self.client.login(username="test", password="test")
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("/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("/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")

View File

@ -1,13 +1,17 @@
# coding=utf-8
from django.contrib import auth
from django.shortcuts import render
from django.db.models import Q
from rest_framework.views import APIView
from utils.shortcuts import serializer_invalid_response, error_response, success_response
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
from .serializers import (UserLoginSerializer, UsernameCheckSerializer,
UserRegisterSerializer, UserChangePasswordSerializer,
EmailCheckSerializer, UserSerializer, EditUserSerializer)
class UserLoginAPIView(APIView):
@ -17,7 +21,7 @@ class UserLoginAPIView(APIView):
---
request_serializer: UserLoginSerializer
"""
serializer = UserLoginSerializer(data=request.DATA)
serializer = UserLoginSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
user = auth.authenticate(username=data["username"], password=data["password"])
@ -38,14 +42,20 @@ class UserRegisterAPIView(APIView):
---
request_serializer: UserRegisterSerializer
"""
serializer = UserRegisterSerializer(data=request.DATA)
serializer = UserRegisterSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
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"注册成功!")
@ -60,7 +70,7 @@ class UserChangePasswordAPIView(APIView):
---
request_serializer: UserChangePasswordSerializer
"""
serializer = UserChangePasswordSerializer(data=request.DATA)
serializer = UserChangePasswordSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
user = auth.authenticate(username=data["username"], password=data["old_password"])
@ -81,7 +91,7 @@ class UsernameCheckAPIView(APIView):
---
request_serializer: UsernameCheckSerializer
"""
serializer = UsernameCheckSerializer(data=request.DATA)
serializer = UsernameCheckSerializer(data=request.data)
if serializer.is_valid():
try:
User.objects.get(username=serializer.data["username"])
@ -89,4 +99,86 @@ class UsernameCheckAPIView(APIView):
except User.DoesNotExist:
return success_response(False)
else:
return serializer_invalid_response(serializer)
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)
class UserAdminAPIView(APIView):
def put(self, request):
"""
用户编辑json api接口
---
request_serializer: EditUserSerializer
response_serializer: UserSerializer
"""
serializer = EditUserSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
user = User.objects.get(id=data["id"])
except User.DoesNotExist:
return error_response(u"该用户不存在!")
try:
user = User.objects.get(username=data["username"])
if user.id != data["id"]:
return error_response(u"昵称已经存在")
except User.DoesNotExist:
pass
user.username = data["username"]
user.real_name = data["real_name"]
user.email = data["email"]
user.admin_type = data["admin_type"]
if data["password"]:
user.set_password(data["password"])
user.save()
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)

View File

@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.

16
admin/middleware.py Normal file
View File

@ -0,0 +1,16 @@
# coding=utf-8
import json
from django.http import HttpResponse, HttpResponseRedirect
class AdminRequiredMiddleware(object):
def process_request(self, request):
path = request.path_info
if path.startswith("/admin/") or path.startswith("/api/admin/"):
if not(request.user.is_authenticated() and request.user.admin_type):
if request.is_ajax():
return HttpResponse(json.dumps({"code": 1, "data": u"请先登录"}),
content_type="application/json")
else:
return HttpResponseRedirect("/login/")

10
admin/test_urls.py Normal file
View File

@ -0,0 +1,10 @@
# coding=utf-8
from django.conf.urls import include, url
from django.views.generic import TemplateView
urlpatterns = [
url(r'^admin/$', TemplateView.as_view(template_name="admin/admin.html"), name="admin_spa_page"),
url(r'^api/admin/test/$', "admin.tests.middleware_test_func"),
url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"),
]

View File

@ -0,0 +1,87 @@
# coding=utf-8
import json
from django.test import TestCase, Client
from django.core.urlresolvers import reverse
from django.http import HttpResponse
from account.models import User
def middleware_test_func(request):
return HttpResponse(json.dumps({"code": 0}))
class AdminRequiredMiddlewareTest(TestCase):
urls = "admin.test_urls"
def setUp(self):
admin_user = User.objects.create(username="test", admin_type=0)
admin_user.set_password("test")
admin_user.save()
admin_user = User.objects.create(username="test1", admin_type=1)
admin_user.set_password("test")
admin_user.save()
super_admin_user = User.objects.create(username="test2", admin_type=2)
super_admin_user.set_password("test")
super_admin_user.save()
self.client = Client()
def test_need_admin_login(self):
url = "/admin/"
response = self.client.get(url)
self.assertRedirects(response, "/login/")
self.client.login(username="test", password="test")
response = self.client.get(url)
self.assertRedirects(response, "/login/")
self.client.logout()
self.client.login(username="test1", password="test")
response = self.client.get(url)
self.assertTemplateUsed(response, "admin/admin.html")
self.client.logout()
self.client.login(username="test2", password="test")
response = self.client.get(url)
self.assertTemplateUsed(response, "admin/admin.html")
def test_need_admin_login_ajax(self):
url = "/api/admin/test/"
response = self.client.get(url, 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(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(json.loads(response.content), {"code": 1, "data": u"请先登录"})
self.client.logout()
self.client.login(username="test1", password="test")
response = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(json.loads(response.content)["code"], 0)
self.client.logout()
self.client.login(username="test2", password="test")
response = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(json.loads(response.content)["code"], 0)
class AdminTemplateViewTest(TestCase):
def setUp(self):
super_admin_user = User.objects.create(username="test", admin_type=2)
super_admin_user.set_password("test")
super_admin_user.save()
self.client = Client()
self.client.login(username="test", password="test")
def test_file_exists(self):
response = self.client.get("/admin/template/index/index.html")
self.assertEqual(response.status_code, 200)
def test_file_does_not_exist(self):
response = self.client.get("/admin/template/index/index123.html")
self.assertEqual(response.status_code, 200)
self.assertHTMLEqual(response.content, u"模板不存在")

View File

@ -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.TEMPLATES[0]["DIRS"][0] + "/admin/" + template_dir + "/" + template_name + ".html"
try:
return HttpResponse(open(path).read(), content_type="text/html")
except IOError:
return HttpResponse(u"模板不存在", content_type="text/html")

View File

@ -0,0 +1,30 @@
# -*- 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)),
('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)),
],
options={
'db_table': 'announcement',
},
),
]

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('group', '0004_merge'),
('announcement', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='announcement',
name='groups',
field=models.ManyToManyField(to='group.Group'),
),
migrations.AddField(
model_name='announcement',
name='is_global',
field=models.BooleanField(default=True),
preserve_default=False,
),
]

26
announcement/models.py Normal file
View File

@ -0,0 +1,26 @@
# coding=utf-8
from django.db import models
from account.models import User
from group.models import Group
class Announcement(models.Model):
# 标题
title = models.CharField(max_length=50)
# 公告的内容 HTML 格式
content = models.TextField()
# 创建时间
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)
# 公告可见范围 0是全局可见 1是部分小组可见需要在下面的字段中存储可见的小组
is_global = models.BooleanField()
groups = models.ManyToManyField(Group)
class Meta:
db_table = "announcement"

View File

@ -0,0 +1,34 @@
# coding=utf-8
from rest_framework import serializers
from account.models import User
from .models import Announcement
class CreateAnnouncementSerializer(serializers.Serializer):
title = serializers.CharField(max_length=50)
content = serializers.CharField(max_length=10000)
is_global = serializers.BooleanField()
groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[])
class AnnouncementSerializer(serializers.ModelSerializer):
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["username"]
created_by = UserSerializer()
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()
is_global = serializers.BooleanField()
groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[])

171
announcement/tests.py Normal file
View File

@ -0,0 +1,171 @@
# coding=utf-8
from django.core.urlresolvers import reverse
from django.test import TestCase
from rest_framework.test import APITestCase, APIClient
from account.models import User
from group.models import Group
from announcement.models import Announcement
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN
class AnnouncementAdminAPITest(APITestCase):
def setUp(self):
self.client = APIClient()
self.url = reverse("announcement_admin_api")
user1 = User.objects.create(username="test1", admin_type=SUPER_ADMIN)
user1.set_password("testaa")
user1.save()
user2 = User.objects.create(username="test2", admin_type=ADMIN)
user2.set_password("testbb")
user2.save()
self.group = Group.objects.create(name="group1", description="des0",
join_group_setting=0, visible=True,
admin=user2)
self.announcement = Announcement.objects.create(title="bb",
content="BB",
created_by=User.objects.get(username="test2"),
is_global=False)
# 以下是发布公告的测试
def test_invalid_format(self):
self.client.login(username="test1", password="testaa")
data = {"title": "test1"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_group_at_least_one(self):
self.client.login(username="test1", password="testaa")
data = {"title": "title0", "content": "content0", "is_global": False}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"至少选择一个小组"})
def test_global_announcement_successfully(self):
self.client.login(username="test1", password="testaa")
data = {"title": "title0", "content": "content0", "is_global": True}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 0, "data": u"公告发布成功!"})
def test_group_announcement_successfully(self):
self.client.login(username="test2", password="testbb")
data = {"title": "title0", "content": "content0", "is_global": False, "groups": [self.group.id]}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 0, "data": u"公告发布成功!"})
def test_global_announcement_does_not_has_privileges(self):
self.client.login(username="test2", password="testbb")
data = {"title": "title0", "content": "content0", "is_global": True}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"只有超级管理员可以创建全局公告"})
# 以下是编辑公告的测试
def test_put_invalid_data(self):
self.client.login(username="test1", password="testaa")
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):
self.client.login(username="test1", password="testaa")
announcement = Announcement.objects.create(title="aa",
content="AA",
created_by=User.objects.get(username="test1"),
is_global=True)
data = {"id": announcement.id + 1, "title": "11", "content": "22",
"visible": True, "is_global": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"公告不存在"})
def test_edit_global_announcement_successfully(self):
self.client.login(username="test1", password="testaa")
data = {"id": self.announcement.id, "title": "11", "content": "22",
"visible": True, "is_global": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0)
def test_edit_group_announcement_successfully(self):
self.client.login(username="test2", password="testbb")
data = {"id": self.announcement.id, "title": "11", "content": "22",
"visible": True, "is_global": False, "groups": [self.group.id]}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0)
self.assertEqual(response.data["data"]["title"], "11")
self.assertEqual(response.data["data"]["content"], "22")
def test_edit_group_at_least_one(self):
self.client.login(username="test1", password="testaa")
data = {"id": self.announcement.id, "title": "title0", "content": "content0",
"visible": True, "is_global": False}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"至少选择一个小组"})
# 以下是公告分页的测试
def test_get_data_successfully(self):
self.client.login(username="test1", password="testaa")
self.assertEqual(self.client.get(self.url).data["code"], 0)
def test_keyword_global_announcement(self):
self.client.login(username="test1", password="testaa")
Announcement.objects.create(title="aa",
content="AA",
created_by=User.objects.get(username="test1"),
visible=True,
is_global=True)
Announcement.objects.create(title="bb",
content="BB",
created_by=User.objects.get(username="test1"),
visible=False,
is_global=True)
response = self.client.get(self.url + "?visible=true")
self.assertEqual(response.data["code"], 0)
for item in response.data["data"]:
self.assertEqual(item["visible"], True)
def test_keyword_group_announcement(self):
self.client.login(username="test2", password="testbb")
Announcement.objects.create(title="aa",
content="AA",
created_by=User.objects.get(username="test2"),
visible=True,
is_global=False)
Announcement.objects.create(title="cc",
content="CC",
created_by=User.objects.get(username="test2"),
visible=False,
is_global=False)
response = self.client.get(self.url + "?visible=true")
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,
is_global=True)
Announcement.objects.create(title="bb",
content="BB",
created_by=User.objects.get(username="test"),
visible=False,
is_global=True)
def test_visit_announcement_successfully(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")

107
announcement/views.py Normal file
View File

@ -0,0 +1,107 @@
# coding=utf-8
from rest_framework.views import APIView
from django.shortcuts import render
from utils.shortcuts import serializer_invalid_response, error_response, success_response
from utils.shortcuts import paginate, error_page
from account.models import SUPER_ADMIN, ADMIN
from group.models import Group
from .models import Announcement
from .serializers import (CreateAnnouncementSerializer, AnnouncementSerializer,
EditAnnouncementSerializer)
def announcement_page(request, announcement_id):
try:
announcement = Announcement.objects.get(id=announcement_id, visible=True)
except Announcement.DoesNotExist:
return error_page(request, u"模板不存在")
return render(request, "oj/announcement/announcement.html", {"announcement": announcement})
class AnnouncementAdminAPIView(APIView):
def post(self, request):
"""
公告发布json api接口
---
request_serializer: CreateAnnouncementSerializer
"""
serializer = CreateAnnouncementSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
groups = []
# 如果不是全局公告就去查询一下小组的id 列表中的内容,注意用户身份
if not data["is_global"]:
if request.user.admin_type == SUPER_ADMIN:
groups = Group.objects.filter(id__in=data["groups"])
else:
groups = Group.objects.filter(id__in=data["groups"], admin=request.user)
if not groups.count():
return error_response(u"至少选择一个小组")
else:
if request.user.admin_type != SUPER_ADMIN:
return error_response(u"只有超级管理员可以创建全局公告")
announcement = Announcement.objects.create(title=data["title"],
content=data["content"],
created_by=request.user,
is_global=data["is_global"])
announcement.groups.add(*groups)
return success_response(u"公告发布成功!")
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:
if request.user.admin_type == SUPER_ADMIN:
announcement = Announcement.objects.get(id=data["id"])
else:
announcement = Announcement.objects.get(id=data["id"], created_by=request.user)
except Announcement.DoesNotExist:
return error_response(u"公告不存在")
groups = []
if not data["is_global"]:
if request.user.admin_type == SUPER_ADMIN:
groups = Group.objects.filter(id__in=data["groups"])
else:
groups = Group.objects.filter(id__in=data["groups"], admin=request.user)
if not groups.count():
return error_response(u"至少选择一个小组")
announcement.title = data["title"]
announcement.content = data["content"]
announcement.visible = data["visible"]
announcement.is_global = data["is_global"]
announcement.save()
# 重建小组和公告的对应关系
announcement.groups.clear()
announcement.groups.add(*groups)
return success_response(AnnouncementSerializer(announcement).data)
else:
return serializer_invalid_response(serializer)
def get(self, request):
"""
公告分页json api接口
---
response_serializer: AnnouncementSerializer
"""
if request.user.admin_type == SUPER_ADMIN:
announcement = Announcement.objects.all().order_by("-last_update_time")
else:
announcement = Announcement.objects.filter(created_by=request.user)
visible = request.GET.get("visible", None)
if visible:
announcement = announcement.filter(visible=(visible == "true"))
return paginate(request, announcement, AnnouncementSerializer)

View File

@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.

86
contest/decorators.py Normal file
View File

@ -0,0 +1,86 @@
# coding=utf-8
from functools import wraps
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.utils.timezone import now
from utils.shortcuts import error_response, error_page
from account.models import SUPER_ADMIN
from .models import Contest
def check_user_contest_permission(func):
@wraps(func)
def _check_user_contest_permission(*args, **kwargs):
"""
这个函数检查当前的这个比赛对于 request 的用户来说能不能参加
需要比较比赛的开始和结束时间比赛是否有密码比赛是不是限定指定小组参加
如果是有密码或者限定指定小组参加的话即使比赛已经结束那么也是可以看到所有的题目和结果的
否则不能看到这个比赛的题目结果排名等等
"""
# CBV 的情况第一个参数是self第二个参数是request
if len(args) == 2:
request = args[-1]
else:
request = args[0]
if not request.user.is_authenticated():
if request.is_ajax():
return error_response(u"请先登录")
else:
return HttpResponseRedirect("/login/")
# kwargs 就包含了url 里面的播或参数
if "contest_id" in kwargs:
contest_id = kwargs["contest_id"]
elif "contest_id" in request.data:
contest_id = request.data["contest_id"]
else:
if request.is_ajax():
return error_response(u"参数错误")
else:
return error_page(request, u"参数错误")
try:
contest = Contest.objects.get(id=contest_id)
except Contest.DoesNotExist:
if request.is_ajax():
return error_response(u"比赛不存在")
else:
return error_page(request, u"比赛不存在")
if request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by:
return func(*args, **kwargs)
# 有密码的公开赛
if contest.contest_type == 2:
# 没有输入过密码
if contest.id not in request.session.get("contests", []):
if request.is_ajax():
return error_response(u"请先输入密码")
else:
return render(request, "oj/contest/no_contest_permission.html",
{"reason": "password_protect", "show_tab": False, "contest": contest})
# 指定小组参加的
if contest.contest_type == 0:
if not contest.groups.filter(id__in=request.user.group_set.all()).exists():
if request.is_ajax():
return error_response(u"只有指定小组的可以参加这场比赛")
else:
return render(request, "oj/contest/no_contest_permission.html",
{"reason": "group_limited", "show_tab": False, "contest": contest})
# 比赛没有开始
if contest.status == 1:
if request.is_ajax():
return error_response(u"比赛还没有开始")
else:
return render(request, "oj/contest/no_contest_permission.html",
{"reason": "contest_not_start", "show_tab": False, "contest": contest})
return func(*args, **kwargs)
return _check_user_contest_permission

View File

@ -0,0 +1,74 @@
# -*- 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),
('group', '0004_merge'),
]
operations = [
migrations.CreateModel(
name='Contest',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('title', models.CharField(unique=True, max_length=40)),
('description', models.TextField()),
('mode', models.IntegerField()),
('show_rank', models.BooleanField()),
('show_user_submission', models.BooleanField()),
('password', models.CharField(max_length=30, null=True, blank=True)),
('contest_type', models.IntegerField()),
('start_time', models.DateTimeField()),
('end_time', models.DateTimeField()),
('create_time', models.DateTimeField(auto_now_add=True)),
('last_updated_time', models.DateTimeField(auto_now=True)),
('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
('groups', models.ManyToManyField(to='group.Group')),
],
options={
'db_table': 'contest',
},
),
migrations.CreateModel(
name='ContestProblem',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('title', models.CharField(max_length=50)),
('description', models.TextField()),
('input_description', models.CharField(max_length=10000)),
('output_description', models.CharField(max_length=10000)),
('samples', models.TextField(blank=True)),
('test_case_id', models.CharField(max_length=40)),
('hint', models.TextField(null=True, blank=True)),
('create_time', models.DateTimeField(auto_now_add=True)),
('time_limit', models.IntegerField()),
('memory_limit', models.IntegerField()),
('visible', models.BooleanField(default=True)),
('total_submit_number', models.IntegerField(default=0)),
('total_accepted_number', models.IntegerField(default=0)),
('sort_index', models.CharField(max_length=30)),
('contest', models.ForeignKey(to='contest.Contest')),
('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'contest_problem',
},
),
migrations.CreateModel(
name='ContestProblemTestCase',
fields=[
('id', models.CharField(max_length=40, serialize=False, primary_key=True, db_index=True)),
('score', models.IntegerField()),
('problem', models.ForeignKey(to='contest.ContestProblem')),
],
options={
'db_table': 'contest_problem_test_case',
},
),
]

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('contest', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='contest',
name='visible',
field=models.BooleanField(default=True),
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('contest', '0002_contest_visible'),
]
operations = [
migrations.AddField(
model_name='contestproblem',
name='difficulty',
field=models.IntegerField(default=1),
preserve_default=False,
),
]

View File

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('contest', '0003_contestproblem_difficulty'),
]
operations = [
migrations.RemoveField(
model_name='contestproblem',
name='difficulty',
),
]

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('contest', '0004_remove_contestproblem_difficulty'),
]
operations = [
migrations.AddField(
model_name='contestproblem',
name='score',
field=models.IntegerField(default=0),
),
]

View File

@ -0,0 +1,31 @@
# -*- 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),
('contest', '0004_remove_contestproblem_difficulty'),
]
operations = [
migrations.CreateModel(
name='ContestSubmission',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('total_submission_number', models.IntegerField(default=1)),
('ac', models.BooleanField()),
('total_time', models.IntegerField(default=0)),
('contest', models.ForeignKey(to='contest.Contest')),
('problem', models.ForeignKey(to='contest.ContestProblem')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'contest_submission',
},
),
]

View File

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('contest', '0005_contestsubmission'),
('contest', '0005_contestproblem_score'),
]
operations = [
]

View File

@ -1,19 +1,91 @@
# coding=utf-8
from django.db import models
from django.utils.timezone import now
from account.models import User
from problem.models import AbstractProblem
from group.models import Group
class Contest(models.Model):
title = models.CharField(max_length=40)
title = models.CharField(max_length=40, unique=True)
description = models.TextField()
is_public = models.BooleanField()
# 比赛模式0 即为是acm模式1 即为是按照总的 ac 题目数量排名模式
mode = models.IntegerField()
# 是否显示排名结果
show_rank = models.BooleanField()
# 是否显示别人的提交记录
show_user_submission = models.BooleanField()
# 只能超级管理员创建公开赛,管理员只能创建小组内部的比赛
# 如果这一项不为空,即为有密码的公开赛,没有密码的可以为小组赛或者是公开赛(此时用比赛的类型来表示)
password = models.CharField(max_length=30, blank=True, null=True)
# 比赛的类型: 0 即为是小组赛1 即为是无密码的公开赛2 即为是有密码的公开赛
contest_type = models.IntegerField()
# 开始时间
start_time = models.DateTimeField()
# 结束时间
end_time = models.DateTimeField()
# 创建时间
create_time = models.DateTimeField(auto_now_add=True)
# 最后修改时间
last_updated_time = models.DateTimeField(auto_now=True)
# 这个比赛是谁创建的
created_by = models.ForeignKey(User)
groups = models.ManyToManyField(Group)
# 是否可见 false的话相当于删除
visible = models.BooleanField(default=True)
@property
def status(self):
if self.start_time > now():
# 没有开始 返回1
return 1
elif self.end_time < now():
# 已经结束 返回0
return -1
else:
# 正在进行 返回0
return 0
class Meta:
db_table = "contest"
class ContestProblem(AbstractProblem):
contest = models.ForeignKey(Contest)
# 比如A B 或者1 2 或者 a b 将按照这个排序
sort_index = models.CharField(max_length=30)
score = models.IntegerField(default=0)
class Meta:
db_table = "contest_problem"
class ContestProblemTestCase(models.Model):
"""
如果比赛是按照通过的测试用例总分计算的话就需要这个model 记录每个测试用例的分数
"""
# 测试用例的id 这个还在测试用例的配置文件里面有对应
id = models.CharField(max_length=40, primary_key=True, db_index=True)
problem = models.ForeignKey(ContestProblem)
score = models.IntegerField()
class Meta:
db_table = "contest_problem_test_case"
class ContestSubmission(models.Model):
"""
用于保存比赛提交和排名的一些数据加快检索速度
"""
user = models.ForeignKey(User)
problem = models.ForeignKey(ContestProblem)
contest = models.ForeignKey(Contest)
total_submission_number = models.IntegerField(default=1)
# 这道题是 AC 还是没过
ac = models.BooleanField()
# 总的时间用于acm 类型的,也需要保存罚时
total_time = models.IntegerField(default=0)
class Meta:
db_table = "contest_submission"

109
contest/serializers.py Normal file
View File

@ -0,0 +1,109 @@
# coding=utf-8
import json
from rest_framework import serializers
from account.models import User
from account.serializers import UserSerializer
from .models import Contest, ContestProblem
class CreateContestSerializer(serializers.Serializer):
title = serializers.CharField(max_length=40)
description = serializers.CharField(max_length=5000)
mode = serializers.IntegerField()
contest_type = serializers.IntegerField()
show_rank = serializers.BooleanField()
show_user_submission = serializers.BooleanField()
password = serializers.CharField(max_length=30, required=False, default=None)
start_time = serializers.DateTimeField()
end_time = serializers.DateTimeField()
groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[])
visible = serializers.BooleanField()
class ContestSerializer(serializers.ModelSerializer):
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["username"]
created_by = UserSerializer()
class Meta:
model = Contest
class EditContestSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(max_length=40)
description = serializers.CharField(max_length=10000)
mode = serializers.IntegerField()
contest_type = serializers.IntegerField()
show_rank = serializers.BooleanField()
show_user_submission = serializers.BooleanField()
password = serializers.CharField(max_length=30, required=False, default=None)
start_time = serializers.DateTimeField()
end_time = serializers.DateTimeField()
groups = serializers.ListField(child=serializers.IntegerField(), required=False, default=[])
visible = serializers.BooleanField()
class ContestProblemSampleSerializer(serializers.ListField):
input = serializers.CharField(max_length=3000)
output = serializers.CharField(max_length=3000)
class JSONField(serializers.Field):
def to_representation(self, value):
return json.loads(value)
class CreateContestProblemSerializer(serializers.Serializer):
contest_id = serializers.IntegerField()
title = serializers.CharField(max_length=50)
description = 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 = ContestProblemSampleSerializer()
test_case_id = serializers.CharField(max_length=40)
time_limit = serializers.IntegerField()
memory_limit = serializers.IntegerField()
hint = serializers.CharField(max_length=3000, allow_blank=True)
score = serializers.IntegerField(required=False, default=0)
sort_index = serializers.CharField(max_length=30)
class ContestProblemSerializer(serializers.ModelSerializer):
class ContestSerializer(serializers.ModelSerializer):
class Meta:
model = Contest
fields = ["title", "id"]
samples = JSONField()
contest = ContestSerializer()
created_by = UserSerializer()
class Meta:
model = ContestProblem
class EditContestProblemSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(max_length=50)
description = 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)
time_limit = serializers.IntegerField()
memory_limit = serializers.IntegerField()
samples = ContestProblemSampleSerializer()
hint = serializers.CharField(max_length=3000, allow_blank=True)
visible = serializers.BooleanField()
sort_index = serializers.CharField(max_length=30)
score = serializers.IntegerField(required=False, default=0)
class ContestPasswordVerifySerializer(serializers.Serializer):
contest_id = serializers.IntegerField()
password = serializers.CharField(max_length=30)

View File

@ -1,3 +1,400 @@
# coding=utf-8
import json
from django.core.urlresolvers import reverse
from django.test import TestCase
# Create your tests here.
from rest_framework.test import APITestCase, APIClient
from account.models import User
from group.models import Group
from contest.models import Contest, ContestProblem
from announcement.models import Announcement
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN
class ContestAdminAPITest(APITestCase):
def setUp(self):
self.client = APIClient()
self.url = reverse('contest_admin_api')
user1 = User.objects.create(username="test1", admin_type=SUPER_ADMIN)
user1.set_password("testaa")
user1.save()
user2 = User.objects.create(username="test2", admin_type=ADMIN)
user2.set_password("testbb")
user2.save()
user3 = User.objects.create(username="test3", admin_type=REGULAR_USER)
user3.set_password("testcc")
user3.save()
self.group = Group.objects.create(name="group1", description="des0",
join_group_setting=0, visible=True,
admin=user2)
self.group2 = Group.objects.create(name="group2", description="des0",
join_group_setting=0, visible=True,
admin=user1)
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=2, show_rank=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z",
password="aacc", created_by=User.objects.get(username="test1"))
self.group_contest = Contest.objects.create(title="titley", description="descriptiony", mode=1,
contest_type=2, show_rank=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z",
password="aacc", created_by=User.objects.get(username="test1"))
# 以下是比赛发布的测试
def test_invalid_format(self):
self.client.login(username="test1", password="testaa")
data = {"title": "test1"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_global_contest_does_not_has_privileges(self):
self.client.login(username="test2", password="testbb")
data = {"title": "title0", "description": "description0", "mode": 1, "contest_type": 2,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "password": "aabb", "visible": True}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"只有超级管理员才可创建公开赛"})
def test_global_contest_password_exists(self):
self.client.login(username="test1", password="testaa")
data = {"title": "title0", "description": "description0", "mode": 1, "contest_type": 2,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "visible": True}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"此比赛为有密码的公开赛,密码不可为空"})
def test_group_contest_group_at_least_one(self):
self.client.login(username="test1", password="testaa")
data = {"title": "title0", "description": "description0", "mode": 1, "contest_type": 0,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "visible": True}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"请至少选择一个小组"})
def test_global_contest_successfully(self):
self.client.login(username="test1", password="testaa")
data = {"title": "title1", "description": "description1", "mode": 1, "contest_type": 2,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "password": "aabb", "visible": True}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 0)
def test_group_contest_super_admin_successfully(self):
self.client.login(username="test1", password="testaa")
data = {"title": "title3", "description": "description3", "mode": 1, "contest_type": 0,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "groups": [self.group.id], "visible": True}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 0)
def test_group_contest_admin_successfully(self):
self.client.login(username="test2", password="testbb")
data = {"title": "title6", "description": "description6", "mode": 2, "contest_type": 0,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "groups": [self.group.id], "visible": True}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 0)
def test_time_error(self):
self.client.login(username="test1", password="testaa")
data = {"title": "title2", "description": "description2", "mode": 1, "contest_type": 2,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T12:00:00.000Z",
"end_time": "2015-08-15T10:00:00.000Z", "password": "aabb", "visible": True}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"比赛的开始时间不能晚于或等于比赛结束的时间"})
def test_contest_has_exists(self):
self.client.login(username="test1", password="testaa")
data = {"title": "titlex", "description": "descriptionx", "mode": 1, "contest_type": 2,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "password": "aabb", "visible": True}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"比赛名已经存在"})
# 以下是编辑比赛的测试
def test_put_invalid_data(self):
self.client.login(username="test1", password="testaa")
data = {"title": "test0", "description": "description0"}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_contest_does_not_exist(self):
self.client.login(username="test1", password="testaa")
data = {"id": self.global_contest.id + 10, "title": "title2", "description": "description2", "mode": 1,
"contest_type": 2, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T12:00:00.000Z", "password": "aabb",
"visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"该比赛不存在!"})
def test_edit_global_contest_successfully(self):
self.client.login(username="test1", password="testaa")
data = {"id": self.global_contest.id, "title": "titlez", "description": "descriptionz", "mode": 1,
"contest_type": 2, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T13:00:00.000Z", "password": "aabb",
"visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0)
self.assertEqual(response.data["data"]["title"], "titlez")
self.assertEqual(response.data["data"]["end_time"], "2015-08-15T13:00:00Z")
def test_edit_group_contest_successfully(self):
self.client.login(username="test1", password="testaa")
data = {"id": self.group_contest.id, "title": "titleyyy", "description": "descriptionyyyy", "mode": 1,
"contest_type": 0, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T13:00:00.000Z",
"groups": [self.group.id], "visible": False}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0)
self.assertEqual(response.data["data"]["title"], "titleyyy")
self.assertEqual(response.data["data"]["end_time"], "2015-08-15T13:00:00Z")
self.assertEqual(response.data["data"]["visible"], False)
def test_edit_group_contest_unsuccessfully(self):
self.client.login(username="test2", password="testbb")
data = {"id": self.group_contest.id, "title": "titleyyy", "description": "descriptionyyyy", "mode": 1,
"contest_type": 0, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T13:00:00.000Z",
"groups": [self.group.id], "visible": False}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_edit_group_at_least_one(self):
self.client.login(username="test1", password="testaa")
data = {"id": self.group_contest.id, "title": "titleyyy", "description": "descriptionyyyy", "mode": 1,
"contest_type": 0, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T13:00:00.000Z", "visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"请至少选择一个小组"})
def test_edit_contest_has_exists(self):
self.client.login(username="test1", password="testaa")
data = {"id": self.global_contest.id, "title": "titley", "description": "descriptiony", "mode": 1,
"contest_type": 2, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T12:00:00.000Z", "password": "aabb",
"visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"该比赛名称已经存在"})
def test_edit_global_contest_does_not_has_privileges(self):
self.client.login(username="test2", password="testbb")
data = {"id": self.global_contest.id, "title": "titlexxxxxxxxx", "description": "descriptionxxxxxx", "mode": 1,
"contest_type": 2, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T10:00:00.000Z", "end_time": "2015-08-15T12:00:00.000Z", "password": "aabb",
"visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"只有超级管理员才可创建公开赛"})
def test_edit_global_contest_password_exists(self):
self.client.login(username="test1", password="testaa")
data = {"id": self.global_contest.id, "title": "title0", "description": "description0", "mode": 1,
"contest_type": 2,
"show_rank": True, "show_user_submission": True, "start_time": "2015-08-15T10:00:00.000Z",
"end_time": "2015-08-15T12:00:00.000Z", "visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"此比赛为有密码的公开赛,密码不可为空"})
def test_edit_time_error(self):
self.client.login(username="test1", password="testaa")
data = {"id": self.global_contest.id, "title": "titleaaaa", "description": "descriptionaaaaa", "mode": 1,
"contest_type": 2, "show_rank": True, "show_user_submission": True,
"start_time": "2015-08-15T12:00:00.000Z", "end_time": "2015-08-15T10:00:00.000Z", "password": "aabb",
"visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"比赛的开始时间不能晚于或等于比赛结束的时间"})
# 以下是比赛分页的测试
def test_get_data_successfully(self):
self.client.login(username="test1", password="testaa")
self.assertEqual(self.client.get(self.url).data["code"], 0)
def test_get_data_successfully_by_normal_admin(self):
self.client.login(username="test2", password="testbb")
self.assertEqual(self.client.get(self.url).data["code"], 0)
def test_keyword_contest(self):
self.client.login(username="test1", password="testaa")
response = self.client.get(self.url + "?visible=true")
self.assertEqual(response.data["code"], 0)
for item in response.data["data"]:
self.assertEqual(item["visible"], True)
def test_query_by_keyword(self):
self.client.login(username="test1", password="testaa")
response = self.client.get(self.url + "?keyword=title1")
self.assertEqual(response.data["code"], 0)
class ContestProblemAdminAPItEST(APITestCase):
def setUp(self):
self.client = APIClient()
self.url = reverse('contest_problem_admin_api')
self.user = User.objects.create(username="test1", admin_type=SUPER_ADMIN)
self.user.set_password("testaa")
self.user.save()
self.user2 = User.objects.create(username="test2", admin_type=ADMIN)
self.user2.set_password("testaa")
self.user2.save()
self.user3 = User.objects.create(username="test3", admin_type=ADMIN)
self.user3.set_password("testaa")
self.user3.save()
self.client.login(username="test1", password="testaa")
self.global_contest = Contest.objects.create(title="titlex", description="descriptionx", mode=1,
contest_type=2, show_rank=True, show_user_submission=True,
start_time="2015-08-15T10:00:00.000Z",
end_time="2015-08-15T12:00:00.000Z",
password="aacc", created_by=User.objects.get(username="test1"))
self.contest_problem = ContestProblem.objects.create(title="titlex",
description="descriptionx",
input_description="input1_description",
output_description="output1_description",
test_case_id="1",
samples=json.dumps([{"input": "1 1", "output": "2"}]),
time_limit=100,
memory_limit=1000,
hint="hint1",
created_by=User.objects.get(username="test1"),
contest=Contest.objects.get(title="titlex"),
sort_index="a")
# 以下是发布比赛题目的测试
def test_invalid_format(self):
data = {"title": "test1"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_release_contest_problem_successfully(self):
data = {"title": "title2",
"description": "description2",
"input_description": "input_description2",
"output_description": "output_description2",
"test_case_id": "1",
"source": "source1",
"samples": [{"input": "1 1", "output": "2"}],
"time_limit": "100",
"memory_limit": "1000",
"hint": "hint1",
"sort_index": "b",
"contest_id": self.global_contest.id}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 0)
def test_contest_does_not_exists(self):
data = {"title": "titlezzzzzzzz",
"description": "descriptionzzzzzzzzzzz",
"input_description": "input_description2",
"output_description": "output_description2",
"test_case_id": "1",
"source": "source1",
"samples": [{"input": "1 1", "output": "2"}],
"time_limit": "100",
"memory_limit": "1000",
"hint": "hint1",
"sort_index": "b",
"contest_id": self.global_contest.id + 10}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"比赛不存在"})
# 以下是编辑比赛题目的测试
def test_invalid_data(self):
data = {"title": "test1"}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_edit_problem_does_not_exist(self):
data = {"id": self.contest_problem.id + 1,
"title": "title2",
"description": "description2",
"input_description": "input_description2",
"output_description": "output_description2",
"test_case_id": "1",
"source": "source1",
"samples": [{"input": "1 1", "output": "2"}],
"time_limit": "100",
"memory_limit": "1000",
"hint": "hint1",
"sort_index": "b",
"visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"该比赛题目不存在!"})
def test_edit_problem_successfully(self):
data = {"id": self.contest_problem.id,
"title": "title2222222",
"description": "description22222222",
"input_description": "input_description2",
"output_description": "output_description2",
"test_case_id": "1",
"source": "source1",
"samples": [{"input": "1 1", "output": "2"}],
"time_limit": "100",
"memory_limit": "1000",
"hint": "hint1",
"sort_index": "b",
"visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0)
# 以下是比赛题目分页的测试
def test_get_data_successfully(self):
self.client.login(username="test1", password="testaa")
self.assertEqual(self.client.get(self.url).data["code"], 0)
def test_get_data_unsuccessfully(self):
self.client.login(username="test1", password="testaa")
self.assertEqual(self.client.get(self.url+"?contest_id=12").data["code"], 1)
def test_keyword_contest(self):
self.client.login(username="test1", password="testaa")
response = self.client.get(self.url + "?visible=true")
self.assertEqual(response.data["code"], 0)
for item in response.data["data"]:
self.assertEqual(item["visible"], True)
def test_query_by_keyword(self):
self.client.login(username="test1", password="testaa")
response = self.client.get(self.url + "?keyword=title1")
self.assertEqual(response.data["code"], 0)
def test_query_contest_problem_does_not_exist(self):
data = {"contest_problem_id": 1000000}
response = self.client.get(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"比赛题目不存在"})
def test_query_contest_problem_exists(self):
data = {"contest_problem_id": self.contest_problem.id}
response = self.client.get(self.url, data=data)
self.assertEqual(response.data["code"], 0)
def test_query_contest_problem_exists_by_contest_id(self):
self.client.login(username="test3", password="testaa")
response = self.client.get(self.url + "?contest_id=1")
self.assertEqual(response.data["code"], 0)
self.assertEqual(len(response.data["data"]), 0)
def test_query_contest_problem_exists_by_normal_admin(self):
self.client.login(username="test2", password="testaa")
data = {"contest_problem_id": self.contest_problem.id}
response = self.client.get(self.url, data=data)
self.assertEqual(response.data["code"], 0)
def test_edit_problem_unsuccessfully_can_not_access(self):
self.client.login(username="test2", password="testaa")
data = {"id": self.contest_problem.id,
"title": "title2222222",
"description": "description22222222",
"input_description": "input_description2",
"output_description": "output_description2",
"test_case_id": "1",
"source": "source1",
"samples": [{"input": "1 1", "output": "2"}],
"time_limit": "100",
"memory_limit": "1000",
"hint": "hint1",
"sort_index": "b",
"visible": True}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 1)

View File

@ -1,3 +1,417 @@
# coding=utf-8
import json
import datetime
from functools import wraps
from django.utils.timezone import now
from django.shortcuts import render
from django.db import IntegrityError
from django.utils import dateparse
from django.db.models import Q, Count, Sum
from django.core.paginator import Paginator
from rest_framework.views import APIView
from utils.shortcuts import (serializer_invalid_response, error_response,
success_response, paginate, rand_str, error_page)
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN, User
from account.decorators import login_required
from group.models import Group
from announcement.models import Announcement
from .models import Contest, ContestProblem, ContestSubmission
from .decorators import check_user_contest_permission
from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer,
CreateContestProblemSerializer, ContestProblemSerializer,
EditContestProblemSerializer, ContestPasswordVerifySerializer,
EditContestProblemSerializer)
class ContestAdminAPIView(APIView):
def post(self, request):
"""
比赛发布json api接口
---
request_serializer: CreateContestSerializer
response_serializer: ContestSerializer
"""
serializer = CreateContestSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
groups = []
# 首先判断比赛的类型: 0 即为是小组赛1 即为是无密码的公开赛2 即为是有密码的公开赛
# 此时为有密码的公开赛,并且此时只能超级管理员才有权限此创建比赛
if data["contest_type"] in [1, 2]:
if request.user.admin_type != SUPER_ADMIN:
return error_response(u"只有超级管理员才可创建公开赛")
if data["contest_type"] == 2:
if not data["password"]:
return error_response(u"此比赛为有密码的公开赛,密码不可为空")
# 没有密码的公开赛 没有密码的小组赛
elif data["contest_type"] == 0:
if request.user.admin_type == SUPER_ADMIN:
groups = Group.objects.filter(id__in=data["groups"])
else:
groups = Group.objects.filter(id__in=data["groups"], admin=request.user)
if not groups.count():
return error_response(u"请至少选择一个小组")
if data["start_time"] >= data["end_time"]:
return error_response(u"比赛的开始时间不能晚于或等于比赛结束的时间")
try:
contest = Contest.objects.create(title=data["title"], description=data["description"],
mode=data["mode"], contest_type=data["contest_type"],
show_rank=data["show_rank"], password=data["password"],
show_user_submission=data["show_user_submission"],
start_time=dateparse.parse_datetime(data["start_time"]),
end_time=dateparse.parse_datetime(data["end_time"]),
created_by=request.user, visible=data["visible"])
except IntegrityError:
return error_response(u"比赛名已经存在")
contest.groups.add(*groups)
return success_response(ContestSerializer(contest).data)
else:
return serializer_invalid_response(serializer)
def put(self, request):
"""
比赛编辑json api接口
---
request_serializer: EditContestSerializer
response_serializer: ContestSerializer
"""
serializer = EditContestSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
groups = []
try:
contest = Contest.objects.get(id=data["id"])
except Contest.DoesNotExist:
return error_response(u"该比赛不存在!")
try:
contest = Contest.objects.get(title=data["title"])
if contest.id != data["id"]:
return error_response(u"该比赛名称已经存在")
except Contest.DoesNotExist:
pass
if data["contest_type"] in [1, 2]:
if request.user.admin_type != SUPER_ADMIN:
return error_response(u"只有超级管理员才可创建公开赛")
if data["contest_type"] == 2:
if not data["password"]:
return error_response(u"此比赛为有密码的公开赛,密码不可为空")
elif data["contest_type"] == 0:
if request.user.admin_type == SUPER_ADMIN:
groups = Group.objects.filter(id__in=data["groups"])
else:
groups = Group.objects.filter(id__in=data["groups"], admin=request.user)
if not groups.count():
return error_response(u"请至少选择一个小组")
if data["start_time"] >= data["end_time"]:
return error_response(u"比赛的开始时间不能晚于或等于比赛结束的时间")
if request.user.admin_type != SUPER_ADMIN and request.user != contest.created_by:
return error_response(u"你无权修改该比赛!")
contest.title = data["title"]
contest.description = data["description"]
contest.mode = data["mode"]
contest.contest_type = data["contest_type"]
contest.show_rank = data["show_rank"]
contest.show_user_submission = data["show_user_submission"]
contest.start_time = dateparse.parse_datetime(data["start_time"])
contest.end_time = dateparse.parse_datetime(data["end_time"])
contest.visible = data["visible"]
contest.password = data["password"]
contest.save()
contest.groups.clear()
contest.groups.add(*groups)
return success_response(ContestSerializer(contest).data)
else:
return serializer_invalid_response(serializer)
def get(self, request):
"""
比赛分页json api接口
---
response_serializer: ContestSerializer
"""
if request.user.admin_type == SUPER_ADMIN:
contest = Contest.objects.all().order_by("-create_time")
else:
contest = Contest.objects.filter(created_by=request.user).order_by("-create_time")
visible = request.GET.get("visible", None)
if visible:
contest = contest.filter(visible=(visible == "true"))
keyword = request.GET.get("keyword", None)
if keyword:
contest = contest.filter(Q(title__contains=keyword) |
Q(description__contains=keyword))
return paginate(request, contest, ContestSerializer)
class ContestProblemAdminAPIView(APIView):
def post(self, request):
"""
比赛题目发布json api接口
---
request_serializer: CreateContestProblemSerializer
response_serializer: ContestProblemSerializer
"""
serializer = CreateContestProblemSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
contest = Contest.objects.get(id=data["contest_id"])
except Contest.DoesNotExist:
return error_response(u"比赛不存在")
contest_problem = ContestProblem.objects.create(title=data["title"],
description=data["description"],
input_description=data["input_description"],
output_description=data["output_description"],
test_case_id=data["test_case_id"],
samples=json.dumps(data["samples"]),
time_limit=data["time_limit"],
memory_limit=data["memory_limit"],
created_by=request.user,
hint=data["hint"],
contest=contest,
sort_index=data["sort_index"],
score=data["score"])
return success_response(ContestProblemSerializer(contest_problem).data)
else:
return serializer_invalid_response(serializer)
def put(self, request):
"""
比赛题目编辑json api接口
---
request_serializer: EditContestProblemSerializer
response_serializer: ContestProblemSerializer
"""
serializer = EditContestProblemSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
contest_problem = ContestProblem.objects.get(id=data["id"])
except ContestProblem.DoesNotExist:
return error_response(u"该比赛题目不存在!")
contest = Contest.objects.get(id=contest_problem.contest_id)
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
return error_response(u"你无权修改该题目!")
contest_problem.title = data["title"]
contest_problem.description = data["description"]
contest_problem.input_description = data["input_description"]
contest_problem.output_description = data["output_description"]
contest_problem.test_case_id = data["test_case_id"]
contest_problem.time_limit = data["time_limit"]
contest_problem.memory_limit = data["memory_limit"]
contest_problem.samples = json.dumps(data["samples"])
contest_problem.hint = data["hint"]
contest_problem.visible = data["visible"]
contest_problem.sort_index = data["sort_index"]
contest_problem.score = data["score"]
contest_problem.save()
return success_response(ContestProblemSerializer(contest_problem).data)
else:
return serializer_invalid_response(serializer)
def get(self, request):
"""
比赛题目分页json api接口
---
response_serializer: ContestProblemSerializer
"""
contest_problem_id = request.GET.get("contest_problem_id", None)
if contest_problem_id:
try:
contest_problem = ContestProblem.objects.get(id=contest_problem_id)
return success_response(ContestProblemSerializer(contest_problem).data)
except ContestProblem.DoesNotExist:
return error_response(u"比赛题目不存在")
if request.user.admin_type == SUPER_ADMIN:
contest_problem = ContestProblem.objects.all().order_by("sort_index")
else:
contest_problem = ContestProblem.objects.filter(created_by=request.user).order_by("sort_index")
visible = request.GET.get("visible", None)
if visible:
contest_problem = contest_problem.filter(visible=(visible == "true"))
keyword = request.GET.get("keyword", None)
if keyword:
contest_problem = contest_problem.filter(Q(title__contains=keyword) |
Q(description__contains=keyword))
contest_id = request.GET.get("contest_id", None)
if contest_id:
try:
contest = Contest.objects.get(id=contest_id)
except Contest.DoesNotExist:
return error_response(u"该比赛不存在!")
contest_problem = contest_problem.filter(contest=contest).order_by("sort_index")
return paginate(request, contest_problem, ContestProblemSerializer)
class ContestPasswordVerifyAPIView(APIView):
@login_required
def post(self, request):
serializer = ContestPasswordVerifySerializer(data=request.data)
if serializer.is_valid():
data = request.data
try:
contest = Contest.objects.get(id=data["contest_id"], contest_type=2)
except Contest.DoesNotExist:
return error_response(u"密码错误")
if data["password"] != contest.password:
return error_response(u" 密码错误")
else:
if "contests" not in request.session:
request.session["contests"] = []
request.session["contests"].append(int(data["contest_id"]))
# https://docs.djangoproject.com/en/dev/topics/http/sessions/#when-sessions-are-saved
request.session.modified = True
return success_response(True)
else:
return serializer_invalid_response(serializer)
@check_user_contest_permission
def contest_page(request, contest_id):
"""
单个比赛的详情页
"""
try:
contest = Contest.objects.get(id=contest_id)
except Contest.DoesNotExist:
return error_page(request, u"比赛不存在")
return render(request, "oj/contest/contest_index.html", {"contest": contest})
@check_user_contest_permission
def contest_problem_page(request, contest_id, contest_problem_id):
"""
单个比赛题目的详情页
"""
try:
contest = Contest.objects.get(id=contest_id)
except Contest.DoesNotExist:
return error_page(request, u"比赛不存在")
try:
contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
except ContestProblem.DoesNotExist:
return error_page(request, u"比赛题目不存在")
warning = u"您已经提交过本题的正确答案,重复提交可能造成时间累计。"
show_warning = False
try:
submission = ContestSubmission.objects.get(user=request.user, contest=contest, problem=contest_problem)
show_warning = submission.ac
except ContestSubmission.DoesNotExist:
pass
# 已经结束
if contest.status == -1:
show_warning = True
warning = u"比赛已经结束"
return render(request, "oj/contest/contest_problem.html", {"contest_problem": contest_problem, "contest": contest,
"samples": json.loads(contest_problem.samples),
"show_warning": show_warning, "warning": warning})
@check_user_contest_permission
def contest_problems_list_page(request, contest_id):
"""
比赛所有题目的列表页
"""
try:
contest_problems = ContestProblem.objects.filter(contest=Contest.objects.get(id=contest_id)).order_by("sort_index")
except Contest.DoesNotExist:
return error_page(request, u"比赛题目不存在")
# 右侧的公告列表
announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time")
return render(request, "oj/contest/contest_problems_list.html", {"contest_problems": contest_problems,
"announcements": announcements,
"contest": {"id": contest_id}})
def contest_list_page(request, page=1):
"""
所有比赛的列表页
"""
# 正常情况
contests = Contest.objects.filter(visible=True).order_by("-create_time")
# 搜索的情况
keyword = request.GET.get("keyword", None)
if keyword:
contests = contests.filter(title__contains=keyword)
# 筛选我能参加的比赛
join = request.GET.get("join", None)
if join:
contests = Contest.objects.filter(Q(contest_type__in=[1, 2]) | Q(groups__in=request.user.group_set.all())).\
filter(end_time__gt=datetime.datetime.now(), start_time__lt=datetime.datetime.now())
paginator = Paginator(contests, 20)
try:
current_page = paginator.page(int(page))
except Exception:
return error_page(request, u"不存在的页码")
previous_page = next_page = None
try:
previous_page = current_page.previous_page_number()
except Exception:
pass
try:
next_page = current_page.next_page_number()
except Exception:
pass
# 右侧的公告列表
announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time")
return render(request, "oj/contest/contest_list.html",
{"contests": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page,
"keyword": keyword, "announcements": announcements,
"join": join})
def _cmp(x, y):
if x["total_ac"] > y["total_ac"]:
return 1
elif x["total_ac"] < y["total_ac"]:
return -1
else:
if x["total_time"] < y["total_time"]:
return 1
else:
return -1
@check_user_contest_permission
def contest_rank_page(request, contest_id):
contest = Contest.objects.get(id=contest_id)
contest_problems = ContestProblem.objects.filter(contest=contest).order_by("sort_index")
result = ContestSubmission.objects.values("user_id").annotate(total_submit=Count("user_id"))
for i in range(0, len(result)):
# 这个人所有的提交
submissions = ContestSubmission.objects.filter(user_id=result[i]["user_id"])
result[i]["submissions"] = {}
for item in submissions:
result[i]["submissions"][item.problem_id] = item
result[i]["total_ac"] = submissions.filter(ac=True).count()
result[i]["user"] = User.objects.get(id=result[i]["user_id"])
result[i]["total_time"] = submissions.filter(ac=True).aggregate(total_time=Sum("total_time"))["total_time"]
return render(request, "oj/contest/contest_rank.html",
{"contest": contest, "contest_problems": contest_problems, "result": sorted(result, cmp=_cmp, reverse=True)})
# Create your views here.

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,11 @@
# coding=utf-8
from rest_framework import serializers
from account.models import User
class CreateContestSubmissionSerializer(serializers.Serializer):
contest_id = serializers.IntegerField()
problem_id = serializers.IntegerField()
language = serializers.IntegerField()
code = serializers.CharField(max_length=3000)

115
contest_submission/views.py Normal file
View File

@ -0,0 +1,115 @@
# coding=utf-8
import json
import redis
from django.shortcuts import render
from django.core.paginator import Paginator
from rest_framework.views import APIView
from judge.judger_controller.tasks import judge
from judge.judger_controller.settings import redis_config
from account.decorators import login_required
from account.models import SUPER_ADMIN
from contest.decorators import check_user_contest_permission
from problem.models import Problem
from contest.models import Contest, ContestProblem
from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate
from submission.models import Submission
from .serializers import CreateContestSubmissionSerializer
class ContestSubmissionAPIView(APIView):
@check_user_contest_permission
def post(self, request):
"""
创建比赛的提交
---
request_serializer: CreateContestSubmissionSerializer
"""
serializer = CreateContestSubmissionSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
contest = Contest.objects.get(id=data["contest_id"])
except Contest.DoesNotExist:
return error_response(u"比赛不存在")
try:
problem = ContestProblem.objects.get(contest=contest, id=data["problem_id"])
# 更新题目提交计数器
problem.total_submit_number += 1
problem.save()
except Problem.DoesNotExist:
return error_response(u"题目不存在")
submission = Submission.objects.create(user_id=request.user.id, language=int(data["language"]),
contest_id=contest.id, code=data["code"], problem_id=problem.id)
try:
judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id)
except Exception:
return error_response(u"提交判题任务失败")
# 增加redis 中判题队列长度的计数器
r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"])
r.incr("judge_queue_length")
return success_response({"submission_id": submission.id})
else:
return serializer_invalid_response(serializer)
@login_required
def contest_problem_my_submissions_list_page(request, contest_id, contest_problem_id):
"""
我比赛单个题目的所有提交列表
"""
try:
Contest.objects.get(id=contest_id)
except Contest.DoesNotExist:
return error_page(request, u"比赛不存在")
try:
contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True)
except Problem.DoesNotExist:
return error_page(request, u"比赛问题不存在")
submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id).order_by("-create_time"). \
values("id", "result", "create_time", "accepted_answer_time", "language")
return render(request, "oj/contest/my_submissions_list.html",
{"submissions": submissions, "problem": contest_problem})
@login_required
def contest_problem_submissions_list_page(request, contest_id, page=1):
"""
单个比赛中的所有提交包含自己和别人自己可查提交结果其他人不可查
"""
try:
contest = Contest.objects.get(id=contest_id)
except Contest.DoesNotExist:
return error_page(request, u"比赛不存在")
# 以下是本场比赛中所有的提交
submissions = Submission.objects.filter(contest_id=contest_id). \
values("id", "result", "create_time", "accepted_answer_time", "language", "user_id").order_by("-create_time")
paginator = Paginator(submissions, 20)
try:
current_page = paginator.page(int(page))
except Exception:
return error_page(request, u"不存在的页码")
previous_page = next_page = None
try:
previous_page = current_page.previous_page_number()
except Exception:
pass
try:
next_page = current_page.next_page_number()
except Exception:
pass
return render(request, "oj/contest/submissions_list.html",
{"submissions": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20,
"contest": contest})

0
group/__init__.py Normal file
View File

View File

@ -0,0 +1,61 @@
# -*- 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='Group',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=30)),
('description', models.TextField()),
('create_time', models.DateTimeField(auto_now_add=True)),
('join_group_setting', models.IntegerField()),
('visible', models.BooleanField(default=True)),
('admin', models.ForeignKey(related_name='my_groups', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'group',
},
),
migrations.CreateModel(
name='JoinGroupRequest',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('message', models.TextField()),
('create_time', models.DateTimeField(auto_now_add=True)),
('status', models.BooleanField(default=False)),
('group', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
('user', models.ForeignKey(related_name='my_join_group_requests', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'join_group_request',
},
),
migrations.CreateModel(
name='UserGroupRelation',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('join_time', models.DateTimeField(auto_now_add=True)),
('group', models.ForeignKey(to='group.Group')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_group_relation',
},
),
migrations.AddField(
model_name='group',
name='members',
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, through='group.UserGroupRelation'),
),
]

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('group', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='group',
name='name',
field=models.CharField(unique=True, max_length=30),
),
migrations.AlterField(
model_name='joingrouprequest',
name='group',
field=models.ForeignKey(to='group.Group'),
),
migrations.AlterUniqueTogether(
name='usergrouprelation',
unique_together=set([('group', 'user')]),
),
]

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('group', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='group',
name='name',
field=models.CharField(unique=True, max_length=30),
),
migrations.AlterField(
model_name='joingrouprequest',
name='group',
field=models.ForeignKey(to='group.Group'),
),
migrations.AlterUniqueTogether(
name='usergrouprelation',
unique_together=set([('group', 'user')]),
),
]

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('group', '0002_auto_20150811_1456'),
]
operations = [
migrations.AlterField(
model_name='group',
name='join_group_setting',
field=models.IntegerField(default=1),
),
]

View File

@ -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 = [
]

View File

41
group/models.py Normal file
View File

@ -0,0 +1,41 @@
# coding=utf-8
from django.db import models
from account.models import User
class Group(models.Model):
name = models.CharField(max_length=30, unique=True)
description = models.TextField()
create_time = models.DateTimeField(auto_now_add=True)
admin = models.ForeignKey(User, related_name="my_groups")
# 0是公开 1是需要申请后加入 2是不允许任何人加入
join_group_setting = models.IntegerField(default=1)
members = models.ManyToManyField(User, through="UserGroupRelation")
# 解散小组后这一项改为False
visible = models.BooleanField(default=True)
class Meta:
db_table = "group"
class UserGroupRelation(models.Model):
group = models.ForeignKey(Group)
user = models.ForeignKey(User)
join_time = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = "user_group_relation"
unique_together = ("group", "user")
class JoinGroupRequest(models.Model):
group = models.ForeignKey(Group)
user = models.ForeignKey(User, related_name="my_join_group_requests")
message = models.TextField()
create_time = models.DateTimeField(auto_now_add=True)
# 是否处理
status = models.BooleanField(default=False)
class Meta:
db_table = "join_group_request"

76
group/serializers.py Normal file
View File

@ -0,0 +1,76 @@
# coding=utf-8
from rest_framework import serializers
from account.models import User
from account.serializers import UserSerializer
from .models import Group, UserGroupRelation, JoinGroupRequest
class CreateGroupSerializer(serializers.Serializer):
name = serializers.CharField(max_length=20)
description = serializers.CharField(max_length=300)
join_group_setting = serializers.IntegerField(min_value=0, max_value=2)
class EditGroupSerializer(serializers.Serializer):
group_id = serializers.IntegerField()
name = serializers.CharField(max_length=20)
description = serializers.CharField(max_length=300)
join_group_setting = serializers.IntegerField()
class CreateJoinGroupRequestSerializer(serializers.Serializer):
group_id = serializers.IntegerField()
message = serializers.CharField(max_length=30)
class JoinGroupRequestSerializer(serializers.ModelSerializer):
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = ["id", "name"]
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["username"]
group = GroupSerializer()
user = UserSerializer()
class Meta:
model = JoinGroupRequest
class GroupSerializer(serializers.ModelSerializer):
members_number = serializers.SerializerMethodField("_get_group_members_number")
def _get_group_members_number(self, group):
return group.members.all().count()
class Meta:
model = Group
exclude = ["members"]
class GroupMemberSerializer(serializers.ModelSerializer):
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["id", "username", "real_name"]
user = UserSerializer()
class Meta:
model = UserGroupRelation
exclude = ["id"]
class EditGroupMemberSerializer(serializers.Serializer):
group_id = serializers.IntegerField()
members = serializers.ListField(child=serializers.IntegerField())
class PutJoinGroupRequestSerializer(serializers.Serializer):
request_id = serializers.IntegerField()
status = serializers.BooleanField()

256
group/tests.py Normal file
View File

@ -0,0 +1,256 @@
# coding=utf-8
import json
from django.core.urlresolvers import reverse
from rest_framework.test import APITestCase, APIClient
from account.models import User, REGULAR_USER, ADMIN, SUPER_ADMIN
from group.models import Group, UserGroupRelation, JoinGroupRequest
class GroupAPITest(APITestCase):
pass
class GroupAdminAPITest(APITestCase):
def _create_group(self, name, join_group_setting):
group = Group.objects.create(name=name, description="des0",
join_group_setting=join_group_setting, visible=True,
admin=self.user)
return group
def setUp(self):
self.client = APIClient()
self.url = reverse('group_admin_api')
self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN)
self.user.set_password("testaa")
self.user.save()
self.group = self._create_group("group1", 0)
self.client.login(username="test", password="testaa")
# 以下是创建小组的测试
def test_invalid_format(self):
data = {"name": "group1"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_create_group_successfully(self):
data = {"name": "group0", "description": "des0", "join_group_setting": "1"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 0)
def test_group_already_exists(self):
data = {"name": "group1", "description": "des0", "join_group_setting": "1"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"小组名已经存在"})
# 以下是修改小组的测试
def test_put_invalid_data(self):
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):
data = {"group_id": self.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_edit_group_successfully(self):
data = {"group_id": self.group.id, "name": "group0", "description": "des0",
"join_group_setting": 2}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 0)
self.assertEqual(response.data["data"]["name"], "group0")
self.assertEqual(response.data["data"]["join_group_setting"], 2)
def test_edit_group_exists(self):
group = self._create_group("group2", 1)
data = {"group_id": group.id, "name": "group1", "description": "des0",
"join_group_setting": 0}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"小组名已经存在"})
# 以下是查询小组列表或者是单个小组时的测试
def test_select_group_does_not_exist(self):
data = {"group_id": self.group.id + 1}
response = self.client.get(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"小组不存在"})
def test_select_group_successfully(self):
data = {"group_id": self.group.id}
response = self.client.get(self.url, data=data)
self.assertEqual(response.data["code"], 0)
def tests_get_all_groups_successfully(self):
self.assertEqual(self.client.get(self.url).data["code"], 0)
class GroupMemberAdminAPITest(APITestCase):
def setUp(self):
self.client = APIClient()
self.url = reverse('group_member_admin_api')
self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN)
self.user.set_password("testaa")
self.user.save()
self.user1 = User.objects.create(username="member1", admin_type=REGULAR_USER)
self.user1.set_password("testxx")
self.user1.save()
self.client.login(username="test", password="testaa")
self.group = Group.objects.create(name="group1", description="des1",
join_group_setting="1", visible="True",
admin=self.user)
UserGroupRelation.objects.create(group=self.group, user=self.user1)
# 以下是查询小组成员的测试
def test_missing_parameter(self):
self.assertEqual(self.client.get(self.url).data, {"code": 1, "data": u"参数错误"})
def test_group_does_not_exist(self):
data = {"group_id": self.group.id + 1}
response = self.client.get(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"小组不存在"})
def test_get_member_list_successfully(self):
data = {"group_id": self.group.id}
response = self.client.get(self.url, data=data)
self.assertEqual(response.data["code"], 0)
# 以下是删除小组成员的测试
def test_invalid_format(self):
data = {"members": [self.user1.id]}
response = self.client.put(self.url, data=json.dumps(data), content_type="application/json")
self.assertEqual(response.data["code"], 1)
def test_del_group_does_not_exist(self):
data = {"group_id": self.group.id + 1, "members": [self.user1.id]}
response = self.client.put(self.url, data=json.dumps(data), content_type="application/json")
self.assertEqual(response.data, {"code": 1, "data": u"小组不存在"})
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"删除成功"})
try:
UserGroupRelation.objects.get(group=self.group, user=self.user1)
raise AssertionError()
except UserGroupRelation.DoesNotExist:
pass
class JoinGroupAPITest(APITestCase):
def _create_group(self, name, join_group_setting):
group = Group.objects.create(name=name, description="des0",
join_group_setting=join_group_setting, visible="True",
admin=self.user)
return group
def setUp(self):
self.client = APIClient()
self.url = reverse('group_join_admin_api')
self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN)
self.user.set_password("testaa")
self.user.save()
self.client.login(username="test", password="testaa")
self.group = self._create_group("group0", 0)
# 以下是用户要加入某个小组的测试
def test_invalid_format(self):
data = {"message": "message1"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_group_does_not_exist(self):
data = {"group_id": self.group.id + 1, "message": "message1"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"小组不存在"})
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"你已经成功的加入该小组"})
# 再加入一遍 已经是小组成员了
data = {"group_id": self.group.id, "message": "message0"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"你已经是小组成员了"})
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)
self.assertEqual(response.data, {"code": 0, "data": u"申请提交成功,请等待审核"})
JoinGroupRequest.objects.get(user=self.user, group=group, status=False)
# 再提交一遍 已经提交过申请,请等待审核
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)
self.assertEqual(response.data, {"code": 1, "data": u"该小组不允许任何人加入"})
# 以下是搜索小组的测试
def test_error_get_data(self):
self.assertEqual(self.client.get(self.url).data["code"], 1)
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"加入成功"})
UserGroupRelation.objects.get(group=self.group, user=self.user1)
# 再加入一次,此时返回的消息应为 加入失败,已经在本小组内
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"加入失败,已经在本小组内"})

246
group/views.py Normal file
View File

@ -0,0 +1,246 @@
# coding=utf-8
from django.shortcuts import render
from django.db import IntegrityError
from rest_framework.views import APIView
from utils.shortcuts import error_response, serializer_invalid_response, success_response, paginate
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN
from account.decorators import login_required
from .models import Group, JoinGroupRequest, UserGroupRelation
from .serializers import (CreateGroupSerializer, EditGroupSerializer,
CreateJoinGroupRequestSerializer, GroupSerializer,
GroupMemberSerializer, EditGroupMemberSerializer,
JoinGroupRequestSerializer, PutJoinGroupRequestSerializer)
class GroupAPIViewBase(object):
def get_group(self, request, group_id):
"""
根据group_id查询指定的小组的信息结合判断用户权限
管理员可以查询所有的小组其他用户查询自己创建的自傲组
"""
if request.user.admin_type == SUPER_ADMIN:
group = Group.objects.get(id=group_id, visible=True)
else:
group = Group.objects.get(id=group_id, visible=True, admin=request.user)
return group
def get_groups(self, request):
"""
如果是超级管理员就返回全部的小组
如果是管理员就返回他创建的全部小组
"""
if request.user.admin_type == SUPER_ADMIN:
groups = Group.objects.filter(visible=True)
else:
groups = Group.objects.filter(admin=request.user, visible=True)
return groups
class GroupAdminAPIView(APIView, GroupAPIViewBase):
def post(self, request):
"""
创建小组的api
---
request_serializer: CreateGroupSerializer
response_serializer: GroupSerializer
"""
serializer = CreateGroupSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
group = Group.objects.create(name=data["name"],
description=data["description"],
join_group_setting=data["join_group_setting"],
admin=request.user)
except IntegrityError:
return error_response(u"小组名已经存在")
return success_response(GroupSerializer(group).data)
else:
return serializer_invalid_response(serializer)
def put(self, request):
"""
修改小组信息的api
---
request_serializer: EditGroupSerializer
response_serializer: GroupSerializer
"""
serializer = EditGroupSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
group = self.get_group(request, data["group_id"])
except Group.DoesNotExist:
return error_response(u"小组不存在")
try:
group.name = data["name"]
group.description = data["description"]
group.join_group_setting = data["join_group_setting"]
group.save()
except IntegrityError:
return error_response(u"小组名已经存在")
return success_response(GroupSerializer(group).data)
else:
return serializer_invalid_response(serializer)
def get(self, request):
"""
查询小组列表或者单个小组的信息查询单个小组需要传递group_id参数否则返回全部
---
response_serializer: GroupSerializer
"""
group_id = request.GET.get("group_id", None)
# 根据 id 查询小组信息
if group_id:
try:
group = self.get_group(request, group_id)
return success_response(GroupSerializer(group).data)
except Group.DoesNotExist:
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)
class GroupMemberAdminAPIView(APIView, GroupAPIViewBase):
def get(self, request):
"""
查询小组成员的api需要传递group_id参数
---
response_serializer: GroupMemberSerializer
"""
group_id = request.GET.get("group_id", None)
if not group_id:
return error_response(u"参数错误")
try:
group = self.get_group(request, group_id)
except Group.DoesNotExist:
return error_response(u"小组不存在")
return paginate(request, UserGroupRelation.objects.filter(group=group), GroupMemberSerializer)
def put(self, request):
"""
删除小组成员的api接口
---
request_serializer: EditGroupMemberSerializer
"""
serializer = EditGroupMemberSerializer(data=request.data)
if serializer.is_valid():
try:
group = self.get_group(request, serializer.data["group_id"])
except Group.DoesNotExist:
return error_response(u"小组不存在")
user_id_list = serializer.data["members"]
UserGroupRelation.objects.filter(group=group, user__id__in=user_id_list).delete()
return success_response(u"删除成功")
else:
return serializer_invalid_response(serializer)
def join_group(user, group):
try:
UserGroupRelation.objects.create(user=user, group=group)
return True
except IntegrityError:
return False
class JoinGroupAPIView(APIView):
# @login_required
def post(self, request):
"""
加入某个小组的api
---
request_serializer: CreateJoinGroupRequestSerializer
"""
serializer = CreateJoinGroupRequestSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
group = Group.objects.get(id=data["group_id"])
except Group.DoesNotExist:
return error_response(u"小组不存在")
if group.join_group_setting == 0:
if join_group(request.user, group):
return success_response(u"你已经成功的加入该小组")
else:
return error_response(u"你已经是小组成员了")
elif group.join_group_setting == 1:
try:
JoinGroupRequest.objects.get(user=request.user, group=group, status=False)
return error_response(u"你已经提交过申请了,请等待审核")
except JoinGroupRequest.DoesNotExist:
JoinGroupRequest.objects.create(user=request.user, group=group, message=data["message"])
return success_response(u"申请提交成功,请等待审核")
elif group.join_group_setting == 2:
return error_response(u"该小组不允许任何人加入")
else:
return serializer_invalid_response(serializer)
def get(self, request):
"""
搜索小组的api需要传递keyword参数
---
response_serializer: GroupSerializer
"""
keyword = request.GET.get("keyword", None)
if not keyword:
return error_response(u"参数错误")
# 搜索包含这个关键词的 没有解散的 而且允许加入的小组
groups = Group.objects.filter(name__contains=keyword, visible=True, join_group_setting__lte=2)
return paginate(request, groups, GroupSerializer)
class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase):
def get(self, request):
"""
返回管理的群的加群请求
---
response_serializer: JoinGroupRequestSerializer
"""
requests = JoinGroupRequest.objects.filter(group=Group.objects.filter(admin=request.user, visible=True),
status=False)
return paginate(request, requests, JoinGroupRequestSerializer)
def put(self, request):
"""
同意或者拒绝加入小组请求
---
request_serializer: PutJoinGroupRequestSerializer
"""
serializer = PutJoinGroupRequestSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
join_request = JoinGroupRequest.objects.get(id=data["request_id"], group__admin=request.user,
status=False)
except JoinGroupRequest.DoesNotExist:
return error_response(u"请求不存在")
join_request.status = True
join_request.save()
if data["status"]:
if join_group(join_request.user, join_request.group):
return success_response(u"加入成功")
else:
return error_response(u"加入失败,已经在本小组内")
else:
return success_response(u"已拒绝")
else:
return serializer_invalid_response(serializer)

0
install/__init__.py Normal file
View File

View File

3
install/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
install/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

23
install/views.py Normal file
View File

@ -0,0 +1,23 @@
# coding=utf-8
from django.shortcuts import render
from django.http import HttpResponse
from account.models import User
from group.models import Group, UserGroupRelation, JoinGroupRequest
def install(request):
for i in range(10):
user = User.objects.create(username="root" + str(i), admin_type=2, real_name="real_name", email="11111@qq.com")
user.set_password("root")
user.save()
for i in range(10):
group = Group.objects.create(name="group" + str(i),
description="description",
admin=User.objects.get(username="root0"))
for i in range(7):
UserGroupRelation.objects.create(user=User.objects.get(username="root" + str(i)), group=group)
for i in range(7, 10):
JoinGroupRequest.objects.create(user=User.objects.get(username="root" + str(i)),
group=group, message=u"你好啊")
return HttpResponse("success")

3
judge/README.md Normal file
View File

@ -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

View File

@ -0,0 +1 @@
# coding=utf-8

0
judge/judger/__init__.py Normal file
View File

View File

@ -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
@ -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) + \
@ -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 <stdio.h>
#include </dev/random>
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 <iostream>
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)
return self_dict

View File

@ -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

View File

@ -2,21 +2,21 @@
languages = {
"1": {
1: {
"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 {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 -Wall -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": {
3: {
"name": "java",
"src_name": "Main.java",
"code": 3,

99
judge/judger/run.py Normal file
View File

@ -0,0 +1,99 @@
# coding=utf-8
import sys
import json
import MySQLdb
from client import JudgeClient
from language import languages
from compiler import compile_
from result import result
from settings import judger_workspace
from settings import submission_db
# 简单的解析命令行参数
# 参数有 -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
submission_id = args[2]
time_limit = args[4]
memory_limit = args[6]
test_case_id = args[8]
def db_conn():
return MySQLdb.connect(db=submission_db["db"],
user=submission_db["user"],
passwd=submission_db["password"],
host=submission_db["host"],
port=submission_db["port"], charset="utf8")
conn = db_conn()
cur = conn.cursor()
cur.execute("select language, code from submission where id = %s", (submission_id,))
data = cur.fetchall()
if not data:
exit()
language_code = data[0][0]
code = data[0][1]
conn.close()
# 将代码写入文件
language = languages[language_code]
src_path = judger_workspace + "run/" + language["src_name"]
f = open(src_path, "w")
f.write(code.encode("utf8"))
f.close()
# 编译
try:
exe_path = compile_(language, src_path, judger_workspace + "run/")
except Exception as e:
print e
conn = db_conn()
cur = conn.cursor()
cur.execute("update submission set result=%s, info=%s where id=%s",
(result["compile_error"], str(e), submission_id))
conn.commit()
exit()
print "Compile successfully"
# 运行
try:
client = JudgeClient(language_code=language_code,
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 + "/")
judge_result = {"result": result["accepted"], "info": client.run(), "accepted_answer_time": None}
for item in judge_result["info"]:
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_time"] = l[-1]["cpu_time"]
except Exception as e:
print e
conn = db_conn()
cur = conn.cursor()
cur.execute("update submission set result=%s, info=%s where id=%s", (result["system_error"], str(e), submission_id))
conn.commit()
exit()
print "Run successfully"
print judge_result
conn = db_conn()
cur = conn.cursor()
cur.execute("update submission set result=%s, info=%s, accepted_answer_time=%s where id=%s",
(judge_result["result"], json.dumps(judge_result["info"]), judge_result["accepted_answer_time"],
submission_id))
conn.commit()
conn.close()

View File

@ -11,5 +11,15 @@ lrun_uid = 1001
# lrun用户组gid
lrun_gid = 1002
#judger工作目录
# judger工作目录
judger_workspace = "/var/judger/"
# 这个是在docker 中访问数据库 ip 不一定和web服务器还有celery的一样
submission_db = {
"host": "192.168.42.1",
"port": 3306,
"db": "oj_submission",
"user": "root",
"password": "mypwd"
}

View File

@ -0,0 +1 @@
celery -A judge.controller worker -l DEBUG

View File

@ -0,0 +1,7 @@
# coding=utf-8
from __future__ import absolute_import
from celery import Celery
from .settings import redis_config
app = Celery("judge", broker='redis://%s:%s/%s' % (redis_config["host"], redis_config["port"], redis_config["db"]),
include=["judge.judger_controller.tasks"])

View File

@ -0,0 +1,26 @@
# coding=utf-8
redis_config = {
"host": "121.42.32.129",
"port": 6379,
"db": 0
}
docker_config = {
"image_name": " a7673b55d263",
"docker_path": "docker",
"shell": True
}
test_case_dir = "/root/test_case/"
source_code_dir = "/root/"
submission_db = {
"host": "127.0.0.1",
"port": 3306,
"db": "oj_submission",
"user": "root",
"password": "root"
}

View File

@ -0,0 +1,42 @@
# coding=utf-8
import json
import redis
import MySQLdb
import subprocess
from ..judger.result import result
from ..judger_controller.celery import app
from settings import docker_config, source_code_dir, test_case_dir, submission_db, redis_config
@app.task
def judge(submission_id, time_limit, memory_limit, test_case_id):
try:
command = "%s run -t -i --privileged --rm=true " \
"-v %s:/var/judger/test_case/ " \
"-v %s:/var/judger/code/ " \
"%s " \
"python judge/judger/run.py " \
"--solution_id %s --time_limit %s --memory_limit %s --test_case_id %s" % \
(docker_config["docker_path"],
test_case_dir,
source_code_dir,
docker_config["image_name"],
submission_id, str(time_limit), str(memory_limit), test_case_id)
subprocess.call(command, shell=docker_config["shell"])
except Exception as e:
print e
conn = MySQLdb.connect(db=submission_db["db"],
user=submission_db["user"],
passwd=submission_db["password"],
host=submission_db["host"],
port=submission_db["port"],
character="utf8")
cur = conn.cursor()
cur.execute("update submission set result=%s, info=%s where id=%s",
(result["system_error"], str(e), submission_id))
conn.commit()
conn.close()
r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"])
r.decr("judge_queue_length")
r.lpush("queue", submission_id)

View File

@ -0,0 +1,12 @@
#include <stdio.h>
int main()
{
int a = 0;
int i = 0;
for(i = 0; i < 9999999999;i++)
{
a += i;
}
printf("%d", a);
return 0;
}

View File

@ -0,0 +1,6 @@
#include <stdio.h>
#include <unistd.h>
int main()
{
}

8
judge/tests/c/success.c Normal file
View File

@ -0,0 +1,8 @@
# include <stdio.h>
int main()
{
int a, b;
scanf("%d %d", &a, &b);
printf("%d", a + b);
return 0;
}

0
monitor/__init__.py Normal file
View File

19
monitor/views.py Normal file
View File

@ -0,0 +1,19 @@
# coding=utf-8
import redis
import datetime
from rest_framework.views import APIView
from judge.judger.result import result
from judge.judger_controller.settings import redis_config
from utils.shortcuts import success_response
from submission.models import Submission
class QueueLengthMonitorAPIView(APIView):
def get(self, request):
r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"])
waiting_number = r.get("judge_queue_length")
if waiting_number is None:
waiting_number = 0
now = datetime.datetime.now()
return success_response({"time": ":".join([str(now.hour), str(now.minute), str(now.second)]),
"count": waiting_number})

1
mq/__init__.py Normal file
View File

@ -0,0 +1 @@
# coding=utf-8

1
mq/models.py Normal file
View File

@ -0,0 +1 @@
# coding=utf-8

1
mq/scripts/__init__.py Normal file
View File

@ -0,0 +1 @@
# coding=utf-8

94
mq/scripts/info.py Normal file
View File

@ -0,0 +1,94 @@
# coding=utf-8
import logging
import redis
from judge.judger_controller.settings import redis_config
from judge.judger.result import result
from submission.models import Submission
from problem.models import Problem
from contest.models import ContestProblem, Contest, ContestSubmission
logger = logging.getLogger("app_info")
class MessageQueue(object):
def __init__(self):
self.conn = redis.StrictRedis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"])
self.queue = 'queue'
def listen_task(self):
while True:
submission_id = self.conn.blpop(self.queue, 0)[1]
logger.debug("receive submission_id: " + submission_id)
try:
submission = Submission.objects.get(id=submission_id)
except Submission.DoesNotExist:
logger.warning("Submission does not exist, submission_id: " + submission_id)
continue
if submission.result == result["accepted"] and not submission.contest_id:
# 更新普通题目的 ac 计数器
try:
problem = Problem.objects.get(id=submission.problem_id)
problem.total_accepted_number += 1
problem.save()
except Problem.DoesNotExist:
logger.warning("Submission problem does not exist, submission_id: " + submission_id)
# 普通题目的话,到这里就结束了
continue
# 能运行到这里的都是比赛题目
try:
contest = Contest.objects.get(id=submission.contest_id)
contest_problem = ContestProblem.objects.get(contest=contest, id=submission.problem_id)
except Contest.DoesNotExist:
logger.warning("Submission contest does not exist, submission_id: " + submission_id)
continue
except ContestProblem.DoesNotExist:
logger.warning("Submission problem does not exist, submission_id: " + submission_id)
continue
try:
contest_submission = ContestSubmission.objects.get(user_id=submission.user_id, contest=contest,
problem_id=contest_problem.id)
if submission.result == result["accepted"]:
# 避免这道题已经 ac 了,但是又重新提交了一遍
if not contest_submission.ac:
# 这种情况是这个题目前处于错误状态,就使用已经存储了的罚时加上这道题的实际用时
logger.debug(contest.start_time)
logger.debug(submission.create_time)
logger.debug((submission.create_time - contest.start_time).total_seconds())
logger.debug(int((submission.create_time - contest.start_time).total_seconds() / 60))
contest_submission.total_time += int((submission.create_time - contest.start_time).total_seconds() / 60)
# 标记为已经通过
contest_submission.ac = True
# 提交次数加1
contest_submission.total_submission_number += 1
# contest problem ac 计数器加1
contest_problem.total_accepted_number += 1
else:
# 如果这个提交是错误的就罚时20分钟
contest_submission.ac = False
contest_submission.total_time += 20
contest_submission.save()
contest_problem.save()
except ContestSubmission.DoesNotExist:
# 第一次提交
is_ac = submission.result == result["accepted"]
if is_ac:
total_time = int((submission.create_time - contest.start_time).total_seconds() / 60)
# 增加题目总的ac数计数器
contest_problem.total_accepted_number += 1
contest_problem.save()
else:
# 没过罚时20分钟
total_time = 20
ContestSubmission.objects.create(user_id=submission.user_id, contest=contest, problem=contest_problem,
ac=is_ac, total_time=total_time)
logger.debug("Start message queue")
MessageQueue().listen_task()

22
oj/db_router.py Normal file
View File

@ -0,0 +1,22 @@
# coding=utf-8
class DBRouter(object):
def db_for_read(self, model, **hints):
if model._meta.app_label == 'submission':
return 'submission'
return "default"
def db_for_write(self, model, **hints):
if model._meta.app_label == 'submission':
return 'submission'
return "default"
def allow_relation(self, obj1, obj2, **hints):
return True
def allow_migrate(self, db, app_label, model=None, **hints):
if app_label == "submission":
return db == app_label
else:
return db == "default"

View File

@ -1,19 +1,33 @@
# 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/"
# 注意这是web 服务器访问的地址,判题端访问的地址不一定一样,因为可能不在一台机器上
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'CONN_MAX_AGE': 1,
'CONN_MAX_AGE': 0.3,
},
'submission': {
'NAME': 'oj_submission',
'ENGINE': 'django.db.backends.mysql',
'HOST': "121.42.32.129",
'POST': 3306,
'USER': 'root',
'PASSWORD': 'mypwd'
}
}
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DEBUG = True
# 同理 这是 web 服务器的上传路径
TEST_CASE_DIR = os.path.join(BASE_DIR, 'test_case/')
DATABASE_ROUTERS = ['oj.db_router.DBRouter']

View File

@ -39,7 +39,6 @@ ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
@ -47,7 +46,17 @@ INSTALLED_APPS = (
'django.contrib.staticfiles',
'account',
'announcement',
'utils',
'group',
'problem',
'admin',
'submission',
'mq',
'contest',
'contest_submission',
'django_extensions',
'rest_framework',
'rest_framework_swagger',
)
@ -61,6 +70,7 @@ MIDDLEWARE_CLASSES = (
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'admin.middleware.AdminRequiredMiddleware'
)
ROOT_URLCONF = 'oj.urls'
@ -68,7 +78,7 @@ ROOT_URLCONF = 'oj.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'template')],
'DIRS': [os.path.join(BASE_DIR, 'template/src')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
@ -106,7 +116,7 @@ STATIC_URL = '/static/'
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static/src/"),)
TEMPLATE_DIRS = (
os.path.join(BASE_DIR, "template"),
os.path.join(BASE_DIR, "template/src"),
)
AUTH_USER_MODEL = 'account.User'
@ -120,10 +130,16 @@ LOGGING = {
# 日志格式
},
'handlers': {
'file_handler': {
'django_error': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': LOG_PATH + 'info.log',
'filename': LOG_PATH + 'django.log',
'formatter': 'standard'
},
'app_info': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': LOG_PATH + 'app_info.log',
'formatter': 'standard'
},
'console': {
@ -133,13 +149,13 @@ LOGGING = {
}
},
'loggers': {
'info_logger': {
'handlers': ['file_handler', "console"],
'app_info': {
'handlers': ['app_info', "console"],
'level': 'DEBUG',
'propagate': True
},
'django.request': {
'handlers': ['file_handler', 'console'],
'handlers': ['django_error', 'console'],
'level': 'DEBUG',
'propagate': True,
},
@ -150,3 +166,8 @@ LOGGING = {
}
},
}
REST_FRAMEWORK = {
'TEST_REQUEST_DEFAULT_FORMAT': 'json'
}

View File

@ -3,19 +3,102 @@ 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,
UserAdminAPIView, UserInfoAPIView)
from announcement.views import AnnouncementAdminAPIView
from contest.views import ContestAdminAPIView, ContestProblemAdminAPIView, ContestPasswordVerifyAPIView
from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView,
JoinGroupAPIView, JoinGroupRequestAdminAPIView)
from admin.views import AdminTemplateView
from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView
from submission.views import SubmissionAPIView, SubmissionAdminAPIView
from contest_submission.views import ContestSubmissionAPIView
from monitor.views import QueueLengthMonitorAPIView
from contest_submission.views import contest_problem_my_submissions_list_page
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/index.html"), name="admin_index_page"),
url(r'^admin/$', TemplateView.as_view(template_name="admin/admin.html"), name="admin_spa_page"),
url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"),
name="add_contest_page"),
url(r'^admin/template/(?P<template_dir>\w+)/(?P<template_name>\w+).html$', AdminTemplateView.as_view(),
name="admin_template"),
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'^announcement/(?P<announcement_id>\d+)/$', "announcement.views.announcement_page",
name="announcement_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"),
url(r'^api/username_check/$', UsernameCheckAPIView.as_view(), name="username_check_api"),
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
url(r'^api/email_check/$', EmailCheckAPIView.as_view(), name="email_check_api"),
url(r'^api/contest/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"),
url(r'^api/contest/submission/$', ContestSubmissionAPIView.as_view(), name="contest_submission_api"),
url(r'^api/submission/$', SubmissionAPIView.as_view(), name="submission_api"),
url(r'^api/admin/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"),
url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"),
url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
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/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_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'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(),
name="join_group_request_admin_api"),
url(r'^api/admin/submission/$', SubmissionAdminAPIView.as_view(), name="submission_admin_api_view"),
url(r'^api/admin/monitor/$', QueueLengthMonitorAPIView.as_view(), name="queue_length_monitor_api"),
url(r'^contest/(?P<contest_id>\d+)/problem/(?P<contest_problem_id>\d+)/$', "contest.views.contest_problem_page",
name="contest_problem_page"),
url(r'^contest/(?P<contest_id>\d+)/problem/(?P<contest_problem_id>\d+)/submissions/$',
"contest_submission.views.contest_problem_my_submissions_list_page",
name="contest_problem_my_submissions_list_page"),
url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"),
url(r'^contest/(?P<contest_id>\d+)/problems/$', "contest.views.contest_problems_list_page",
name="contest_problems_list_page"),
url(r'^contest/(?P<contest_id>\d+)/submissions/$', "contest_submission.views.contest_problem_submissions_list_page",
name="contest_problem_submissions_list_page"),
url(r'^contest/(?P<contest_id>\d+)/submissions/(?P<page>\d+)/$',
"contest_submission.views.contest_problem_submissions_list_page", name="contest_problem_submissions_list_page"),
url(r'^contests/$', "contest.views.contest_list_page", name="contest_list_page"),
url(r'^contests/(?P<page>\d+)/$', "contest.views.contest_list_page", name="contest_list_page"),
url(r'^contest/(?P<contest_id>\d+)/$', "contest.views.contest_page", name="contest_page"),
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
url(r'^problems/$', "problem.views.problem_list_page", name="problem_list_page"),
url(r'^problems/(?P<page>\d+)/$', "problem.views.problem_list_page", name="problem_list_page"),
url(r'^problem/(?P<problem_id>\d+)/submissions/$', "submission.views.problem_my_submissions_list_page",
name="problem_my_submissions_page"),
url(r'^submission/(?P<submission_id>\w+)/$', "submission.views.my_submission", name="my_submission_page"),
url(r'^submissions/$', "submission.views.my_submission_list_page", name="my_submission_list_page"),
url(r'^submissions/(?P<page>\d+)/$', "submission.views.my_submission_list_page", name="my_submission_list_page"),
url(r'^contest/(?P<contest_id>\d+)/rank/$', "contest.views.contest_rank_page", name="contest_rank_page")
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"),
]

View File

@ -0,0 +1,55 @@
# -*- 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='Problem',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('title', models.CharField(max_length=50)),
('description', models.TextField()),
('sample', models.TextField(blank=True)),
('test_case_id', models.CharField(max_length=40)),
('hint', models.TextField(null=True, blank=True)),
('create_time', models.DateTimeField(auto_now_add=True)),
('last_update_time', models.DateTimeField(auto_now=True)),
('source', models.CharField(max_length=30, null=True, blank=True)),
('time_limit', models.IntegerField()),
('memory_limit', models.IntegerField()),
('visible', models.BooleanField(default=True)),
('total_submit_number', models.IntegerField(default=0)),
('total_accepted_number', models.IntegerField(default=0)),
('difficulty', models.IntegerField()),
('created_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='ProblemTag',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=30)),
('description', models.CharField(max_length=50)),
],
options={
'db_table': 'problem_tag',
},
),
migrations.AddField(
model_name='problem',
name='tags',
field=models.ManyToManyField(to='problem.ProblemTag', null=True),
),
]

View File

@ -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',
),
]

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('problem', '0002_remove_problemtag_description'),
]
operations = [
migrations.RenameField(
model_name='problem',
old_name='sample',
new_name='samples',
),
]

View File

@ -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'),
),
]

View File

@ -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,
),
]

View File

@ -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',
),
]

View File

@ -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 = [
]

View File

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('problem', '0006_merge'),
]
operations = [
migrations.RemoveField(
model_name='problem',
name='last_update_time',
),
]

View File

@ -5,7 +5,10 @@ from account.models import User
class ProblemTag(models.Model):
pass
name = models.CharField(max_length=30)
class Meta:
db_table = "problem_tag"
class AbstractProblem(models.Model):
@ -13,22 +16,22 @@ class AbstractProblem(models.Model):
title = models.CharField(max_length=50)
# 问题描述 HTML 格式
description = models.TextField()
# 输入描述
input_description = models.CharField(max_length=10000)
# 输出描述
output_description = models.CharField(max_length=10000)
# 样例输入 可能会存储 json 格式的数据
sample_input = models.TextField(blank=True)
# 样例输出 同上
sample_output = models.TextField(blank=True)
samples = models.TextField(blank=True)
# 测试用例id 这个id 可以用来拼接得到测试用例的文件存储位置
test_case_id = models.CharField(max_length=40)
# 提示
hint = models.TextField(blank=True)
hint = models.TextField(blank=True, null=True)
# 创建时间
create_time = models.DateTimeField(auth_now_add=True)
create_time = models.DateTimeField(auto_now_add=True)
# 最后更新时间
last_update_time = models.DateTimeField(auto_now=True)
# last_update_time = models.DateTimeField(auto_now=True)
# 这个题是谁创建的
created_by = models.ForeignKey(User)
# 来源
source = models.CharField(max_length=30, blank=True)
# 时间限制 单位是毫秒
time_limit = models.IntegerField()
# 内存限制 单位是MB
@ -39,14 +42,15 @@ class AbstractProblem(models.Model):
total_submit_number = models.IntegerField(default=0)
# 通过数量
total_accepted_number = models.IntegerField(default=0)
# 标签
tags = models.ManyToManyField(ProblemTag, null=True)
# 难度 0 - n
difficulty = models.IntegerField()
class Meta:
abstract = True
class Problem(AbstractProblem):
pass
# 难度 0 - n
difficulty = models.IntegerField()
# 标签
tags = models.ManyToManyField(ProblemTag)
# 来源
source = models.CharField(max_length=30, blank=True, null=True)

74
problem/serizalizers.py Normal file
View File

@ -0,0 +1,74 @@
# coding=utf-8
import json
from rest_framework import serializers
from account.models import User
from .models import Problem, ProblemTag
class ProblemSampleSerializer(serializers.ListField):
input = serializers.CharField(max_length=3000)
output = serializers.CharField(max_length=3000)
class JSONField(serializers.Field):
def to_representation(self, value):
return json.loads(value)
class CreateProblemSerializer(serializers.Serializer):
title = serializers.CharField(max_length=50)
description = 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)
source = serializers.CharField(max_length=30, required=False, default=None)
time_limit = serializers.IntegerField()
memory_limit = serializers.IntegerField()
difficulty = serializers.IntegerField()
tags = serializers.ListField(child=serializers.CharField(max_length=10))
hint = serializers.CharField(max_length=3000, allow_blank=True)
class ProblemTagSerializer(serializers.ModelSerializer):
class Meta:
model = ProblemTag
class ProblemSerializer(serializers.ModelSerializer):
samples = JSONField()
tags = ProblemTagSerializer(many=True)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["username"]
created_by = UserSerializer()
class Meta:
model = Problem
class EditProblemSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(max_length=50)
description = serializers.CharField(max_length=10000)
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()
memory_limit = serializers.IntegerField()
difficulty = serializers.IntegerField()
tags = serializers.ListField(child=serializers.CharField(max_length=20))
samples = ProblemSampleSerializer()
hint = serializers.CharField(max_length=3000, allow_blank=True)
visible = serializers.BooleanField()
class CreateProblemTagSerializer(serializers.Serializer):
name = serializers.CharField(max_length=10)

View File

@ -1,6 +1,191 @@
# coding=utf-8
from django.test import TestCase
import json
from django.test import TestCase, Client
from django.core.urlresolvers import reverse
from rest_framework.test import APITestCase, APIClient
from account.models import User, SUPER_ADMIN
from problem.models import Problem, ProblemTag
class ProblemPageTest(TestCase):
pass
def setUp(self):
self.client = APIClient()
self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN)
self.user.set_password("testaa")
self.user.save()
self.client.login(username="test", password="testaa")
self.problem = Problem.objects.create(title="title1",
description="description1",
input_description="input1_description",
output_description="output1_description",
test_case_id="1",
source="source1",
samples=json.dumps([{"input": "1 1", "output": "2"}]),
time_limit=100,
memory_limit=1000,
difficulty=1,
hint="hint1",
created_by=User.objects.get(username="test"))
def test_visit_problem_successfully(self):
response = self.client.get('/problem/1/')
self.assertEqual(response.status_code, 200)
def test_problem_does_not_exist(self):
response = self.client.get('/problem/3/')
self.assertTemplateUsed(response, "utils/error.html")
class ProblemAdminTest(APITestCase):
def _create_data(self, problem_id, visible, tags):
data = {"id": problem_id,
"title": "title0",
"description": "description0",
"input_description": "input_description0",
"output_description": "output_description0",
"test_case_id": "1",
"source": "source1",
"samples": [{"input": "1 1", "output": "2"}],
"time_limit": "100",
"memory_limit": "1000",
"difficulty": "1",
"hint": "hint1",
"visible": visible,
"tags": tags}
return data
def setUp(self):
self.client = APIClient()
self.url = reverse("problem_admin_api")
self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN)
self.user.set_password("testaa")
self.user.save()
self.client.login(username="test", password="testaa")
ProblemTag.objects.create(name="tag1")
ProblemTag.objects.create(name="tag2")
self.problem = Problem.objects.create(title="title1",
description="description1",
input_description="input1_description",
output_description="output1_description",
test_case_id="1",
source="source1",
samples=json.dumps([{"input": "1 1", "output": "2"}]),
time_limit=100,
memory_limit=1000,
difficulty=1,
hint="hint1",
created_by=User.objects.get(username="test"))
# 以下是发布题目的测试
def test_invalid_format(self):
data = {"title": "test1"}
response = self.client.post(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_release_problem_successfully(self):
data = {"title": "title2",
"description": "description2",
"input_description": "input_description2",
"output_description": "output_description2",
"test_case_id": "1",
"source": "source1",
"samples": [{"input": "1 1", "output": "2"}],
"time_limit": "100",
"memory_limit": "1000",
"difficulty": "1",
"hint": "hint1",
"tags": [1]}
response = self.client.post(self.url, data=json.dumps(data), content_type="application/json")
self.assertEqual(response.data["code"], 0)
# 以下是编辑题目的测试
def test_invalid_data(self):
data = {"title": "test0"}
response = self.client.put(self.url, data=data)
self.assertEqual(response.data["code"], 1)
def test_problem_does_not_exist(self):
tags = ProblemTag.objects.filter(id__in=[1])
self.problem.tags.add(*tags)
data = self._create_data(2, False, [1])
response = self.client.put(self.url, data=json.dumps(data), content_type="application/json")
self.assertEqual(response.data, {"code": 1, "data": u"该题目不存在!"})
def test_edit_problem_successfully(self):
tags = ProblemTag.objects.filter(id__in=[1])
self.problem.tags.add(*tags)
data = self._create_data(1, True, [1, 2])
problem = Problem.objects.get(id=data["id"])
problem.tags.remove(*problem.tags.all())
problem.tags.add(*ProblemTag.objects.filter(id__in=data["tags"]))
response = self.client.put(self.url, data=json.dumps(data), content_type="application/json")
self.assertEqual(response.data["code"], 0)
# 以下是题目分页的测试
def test_success_get_data(self):
self.assertEqual(self.client.get(self.url).data["code"], 0)
def test_query_by_keyword(self):
response = self.client.get(self.url + "?keyword=title1")
self.assertEqual(response.data["code"], 0)
def test_query_by_visible(self):
response = self.client.get(self.url + "?visible=true")
self.assertEqual(response.data["code"], 0)
for item in response.data["data"]:
self.assertEqual(item["visible"], True)
def test_query_problem_does_not_exist(self):
data = {"problem_id": 2}
response = self.client.get(self.url, data=data)
self.assertEqual(response.data, {"code": 1, "data": u"题目不存在"})
def test_query_problem_exists(self):
data = {"problem_id": 1}
response = self.client.get(self.url, data=data)
self.assertEqual(response.data["code"], 0)
class ProblemTagAdminAPITest(APITestCase):
def setUp(self):
self.client = APIClient()
self.url = reverse('problem_tag_admin_api')
self.user = User.objects.create(username="testx", admin_type=SUPER_ADMIN)
self.user.set_password("testxx")
self.user.save()
self.client.login(username="testx", password="testxx")
ProblemTag.objects.create(name="tag1")
# 以下是返回所有的问题的标签
def test_get_all_problem_tag_successfully(self):
self.assertEqual(self.client.get(self.url).data["code"], 0)
class ProblemListPageTest(TestCase):
def setUp(self):
self.client = Client()
self.url = reverse('problem_list_page', kwargs={"page": 1})
self.user = User.objects.create(username="test", admin_type=SUPER_ADMIN)
self.user.set_password("testaa")
self.user.save()
self.client.login(username="test", password="testaa")
ProblemTag.objects.create(name="tag1")
ProblemTag.objects.create(name="tag2")
self.problem = Problem.objects.create(title="title1",
description="description1",
input_description="input1_description",
output_description="output1_description",
test_case_id="1",
source="source1",
samples=json.dumps([{"input": "1 1", "output": "2"}]),
time_limit=100,
memory_limit=1000,
difficulty=1,
hint="hint1",
created_by=User.objects.get(username="test"))

View File

@ -1,7 +1,263 @@
# coding=utf-8
import zipfile
import re
import os
import hashlib
import json
from django.shortcuts import render
from django.db.models import Q, Count
from django.core.paginator import Paginator
from rest_framework.views import APIView
from django.conf import settings
from announcement.models import Announcement
from utils.shortcuts import (serializer_invalid_response, error_response,
success_response, paginate, rand_str, error_page)
from .serizalizers import (CreateProblemSerializer, EditProblemSerializer, ProblemSerializer,
ProblemTagSerializer, CreateProblemTagSerializer)
from .models import Problem, ProblemTag
def problem_page(request, problem_id):
# todo
return render(request, "oj/problem/problem.html")
try:
problem = Problem.objects.get(id=problem_id, visible=True)
except Problem.DoesNotExist:
return error_page(request, u"题目不存在")
return render(request, "oj/problem/problem.html", {"problem": problem, "samples": json.loads(problem.samples)})
class ProblemTagAdminAPIView(APIView):
def get(self, request):
return success_response(ProblemTagSerializer(ProblemTag.objects.all(), many=True).data)
class ProblemAdminAPIView(APIView):
def post(self, request):
"""
题目发布json api接口
---
request_serializer: CreateProblemSerializer
response_serializer: ProblemSerializer
"""
serializer = CreateProblemSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
problem = Problem.objects.create(title=data["title"],
description=data["description"],
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"]),
time_limit=data["time_limit"],
memory_limit=data["memory_limit"],
difficulty=data["difficulty"],
created_by=request.user,
hint=data["hint"])
for tag in data["tags"]:
try:
tag = ProblemTag.objects.get(name=tag)
except ProblemTag.DoesNotExist:
tag = ProblemTag.objects.create(name=tag)
problem.tags.add(tag)
return success_response(ProblemSerializer(problem).data)
else:
return serializer_invalid_response(serializer)
def put(self, request):
"""
题目编辑json api接口
---
request_serializer: EditProblemSerializer
response_serializer: ProblemSerializer
"""
serializer = EditProblemSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
try:
problem = Problem.objects.get(id=data["id"])
except Problem.DoesNotExist:
return error_response(u"该题目不存在!")
problem.title = data["title"]
problem.description = data["description"]
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"]
problem.memory_limit = data["memory_limit"]
problem.difficulty = data["difficulty"]
problem.samples = json.dumps(data["samples"])
problem.hint = data["hint"]
problem.visible = data["visible"]
# 删除原有的标签的对应关系
problem.tags.remove(*problem.tags.all())
# 重新添加所有的标签
for tag in data["tags"]:
try:
tag = ProblemTag.objects.get(name=tag)
except ProblemTag.DoesNotExist:
tag = ProblemTag.objects.create(name=tag)
problem.tags.add(tag)
problem.save()
return success_response(ProblemSerializer(problem).data)
else:
return serializer_invalid_response(serializer)
def get(self, request):
"""
题目分页json api接口
---
response_serializer: ProblemSerializer
"""
problem_id = request.GET.get("problem_id", None)
if problem_id:
try:
problem = Problem.objects.get(id=problem_id)
return success_response(ProblemSerializer(problem).data)
except Problem.DoesNotExist:
return error_response(u"题目不存在")
problem = Problem.objects.all().order_by("-create_time")
visible = request.GET.get("visible", None)
if visible:
problem = problem.filter(visible=(visible == "true"))
keyword = request.GET.get("keyword", None)
if keyword:
problem = problem.filter(Q(title__contains=keyword) |
Q(description__contains=keyword))
return paginate(request, problem, ProblemSerializer)
class TestCaseUploadAPIView(APIView):
def _is_legal_test_case_file_name(self, file_name):
# 正整数开头的 .in 或者.out 结尾的
regex = r"^[1-9]\d*\.(in|out)$"
return re.compile(regex).match(file_name) is not None
def post(self, request):
if "file" not in request.FILES:
return error_response(u"文件上传失败")
f = request.FILES["file"]
tmp_zip = "/tmp/" + rand_str() + ".zip"
with open(tmp_zip, "wb") as test_case_zip:
for chunk in f:
test_case_zip.write(chunk)
test_case_file = zipfile.ZipFile(tmp_zip, 'r')
name_list = test_case_file.namelist()
l = []
# 如果文件是直接打包的那么name_list 就是["1.in", "1.out"]这样的
# 如果文件还有一层文件夹test_case那么name_list就是["test_case/", "test_case/1.in", "test_case/1.out"]
# 现在暂时只支持第一种,先判断一下是什么格式的
# 第一种格式的
if "1.in" in name_list and "1.out" in name_list:
for file_name in name_list:
if self._is_legal_test_case_file_name(file_name):
name = file_name.split(".")
# 有了.in 判断对应的.out 在不在
if name[1] == "in":
if (name[0] + ".out") in name_list:
l.append(file_name)
else:
return error_response(u"测试用例文件不完整,缺少" + name[0] + ".out")
else:
# 有了.out 判断对应的 .in 在不在
if (name[0] + ".in") in name_list:
l.append(file_name)
else:
return error_response(u"测试用例文件不完整,缺少" + name[0] + ".in")
problem_test_dir = rand_str()
test_case_dir = settings.TEST_CASE_DIR + problem_test_dir + "/"
# 得到了合法的测试用例文件列表 然后去解压缩
os.mkdir(test_case_dir)
for name in l:
f = open(test_case_dir + name, "wb")
f.write(test_case_file.read(name).replace("\r\n", "\n"))
f.close()
l.sort()
file_info = {"test_case_number": len(l) / 2, "test_cases": {}}
# 计算输出文件的md5
for i in range(len(l) / 2):
md5 = hashlib.md5()
f = open(test_case_dir + str(i + 1) + ".out", "r")
while True:
data = f.read(2 ** 8)
if not data:
break
md5.update(data)
file_info["test_cases"][str(i + 1)] = {"input_name": str(i + 1) + ".in",
"output_name": str(i + 1) + ".out",
"output_md5": md5.hexdigest(),
"output_size": os.path.getsize(test_case_dir + str(i + 1) + ".out")}
# 写入配置文件
open(test_case_dir + "info", "w").write(json.dumps(file_info))
return success_response({"test_case_id": problem_test_dir,
"file_list": {"input": l[0::2],
"output": l[1::2]}})
else:
return error_response(u"测试用例压缩文件格式错误,请保证测试用例文件在根目录下直接压缩")
def problem_list_page(request, page=1):
# 正常情况
problems = Problem.objects.filter(visible=True)
# 搜索的情况
keyword = request.GET.get("keyword", None)
if keyword:
problems = problems.filter(title__contains=keyword)
# 按照标签筛选
tag_text = request.GET.get("tag", None)
if tag_text:
try:
tag = ProblemTag.objects.get(name=tag_text)
except ProblemTag.DoesNotExist:
return error_page(request, u"标签不存在")
problems = tag.problem_set.all()
paginator = Paginator(problems, 20)
try:
current_page = paginator.page(int(page))
except Exception:
return error_page(request, u"不存在的页码")
previous_page = next_page = None
try:
previous_page = current_page.previous_page_number()
except Exception:
pass
try:
next_page = current_page.next_page_number()
except Exception:
pass
# 右侧的公告列表
announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time")
# 右侧标签列表 按照关联的题目的数量排序 排除题目数量为0的
tags = ProblemTag.objects.annotate(problem_number=Count("problem")).filter(problem_number__gt=0).order_by("-problem_number")
return render(request, "oj/problem/problem_list.html",
{"problems": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page,
"keyword": keyword, "tag": tag_text,
"announcements": announcements, "tags": tags})

Some files were not shown because too many files have changed in this diff Show More