Merge branch 'dev' into sxw-dev
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -56,3 +56,5 @@ db.sqlite3
|
||||
.DS_Store
|
||||
log/
|
||||
release/
|
||||
tmp/
|
||||
test_case/
|
||||
@@ -22,7 +22,6 @@ class Migration(migrations.Migration):
|
||||
('join_group_setting', models.IntegerField()),
|
||||
('visible', models.BooleanField(default=True)),
|
||||
('admin', models.ForeignKey(related_name='my_groups', to=settings.AUTH_USER_MODEL)),
|
||||
('members', models.ManyToManyField(to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'group',
|
||||
@@ -42,4 +41,21 @@ class Migration(migrations.Migration):
|
||||
'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'),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -11,7 +11,7 @@ class Group(models.Model):
|
||||
admin = models.ForeignKey(User, related_name="my_groups")
|
||||
# 0是公开 1是需要申请后加入 2是不允许任何人加入
|
||||
join_group_setting = models.IntegerField()
|
||||
members = models.ManyToManyField(User)
|
||||
members = models.ManyToManyField(User, through="UserGroupRelation")
|
||||
# 解散小组后,这一项改为False
|
||||
visible = models.BooleanField(default=True)
|
||||
|
||||
@@ -19,6 +19,15 @@ class Group(models.Model):
|
||||
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"
|
||||
|
||||
|
||||
class JoinGroupRequest(models.Model):
|
||||
group = models.ForeignKey(User)
|
||||
user = models.ForeignKey(User, related_name="my_join_group_requests")
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# coding=utf-8
|
||||
from rest_framework import serializers
|
||||
|
||||
from .models import Group
|
||||
from account.serializers import UserSerializer
|
||||
from .models import Group, UserGroupRelation
|
||||
|
||||
|
||||
class CreateGroupSerializer(serializers.Serializer):
|
||||
@@ -11,13 +12,14 @@ class CreateGroupSerializer(serializers.Serializer):
|
||||
|
||||
|
||||
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 JoinGroupRequestSerializer(serializers.Serializer):
|
||||
group = serializers.IntegerField()
|
||||
group_id = serializers.IntegerField()
|
||||
message = serializers.CharField(max_length=30)
|
||||
|
||||
|
||||
@@ -25,3 +27,16 @@ class GroupSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Group
|
||||
exclude = ["members"]
|
||||
|
||||
|
||||
class GroupMemberSerializer(serializers.ModelSerializer):
|
||||
user = UserSerializer()
|
||||
|
||||
class Meta:
|
||||
model = UserGroupRelation
|
||||
exclude = ["id", "group"]
|
||||
|
||||
|
||||
class EditGroupMemberSerializer(serializers.Serializer):
|
||||
group_id = serializers.IntegerField()
|
||||
members = serializers.ListField(child=serializers.IntegerField())
|
||||
129
group/views.py
129
group/views.py
@@ -5,18 +5,45 @@ 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
|
||||
from .models import Group, JoinGroupRequest, UserGroupRelation
|
||||
from .serializers import (CreateGroupSerializer, EditGroupSerializer,
|
||||
JoinGroupRequestSerializer, GroupSerializer)
|
||||
JoinGroupRequestSerializer, GroupSerializer,
|
||||
GroupMemberSerializer, EditGroupMemberSerializer)
|
||||
|
||||
|
||||
class GroupAdminAPIView(APIView):
|
||||
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():
|
||||
@@ -34,12 +61,13 @@ class GroupAdminAPIView(APIView):
|
||||
修改小组信息的api
|
||||
---
|
||||
request_serializer: EditGroupSerializer
|
||||
response_serializer: GroupSerializer
|
||||
"""
|
||||
serializer = EditGroupSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
try:
|
||||
group = Group.objects.get(id=data["id"], admin=request.user)
|
||||
group = self.get_group(request, data["group_id"])
|
||||
except Group.DoesNotExist:
|
||||
return error_response(u"小组不存在")
|
||||
group.name = data["name"]
|
||||
@@ -52,21 +80,96 @@ class GroupAdminAPIView(APIView):
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
查询小组列表或者单个小组的信息
|
||||
查询小组列表或者单个小组的信息,查询单个小组需要传递group_id参数,否则返回全部
|
||||
---
|
||||
response_serializer: GroupSerializer
|
||||
"""
|
||||
group_id = request.GET.get("group_id", None)
|
||||
if group_id:
|
||||
try:
|
||||
if request.user.admin_type == SUPER_ADMIN:
|
||||
group = Group.object.get(id=group_id)
|
||||
else:
|
||||
group = Group.object.get(id=group_id, admin=request.user)
|
||||
group = self.get_group(request, group_id)
|
||||
return success_response(GroupSerializer(group).data)
|
||||
except Group.DoesNotExist:
|
||||
return error_response(u"小组不存在")
|
||||
else:
|
||||
if request.user.admin_type == SUPER_ADMIN:
|
||||
groups = Group.objects.filter(visible=True)
|
||||
else:
|
||||
groups = Group.objects.filter(admin=request.user, visible=True)
|
||||
groups = self.get_groups(request)
|
||||
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):
|
||||
return UserGroupRelation.objects.create(user=user, group=group)
|
||||
|
||||
|
||||
class JoinGroupAPIView(APIView):
|
||||
@login_required
|
||||
def post(self, request):
|
||||
"""
|
||||
加入某个小组的api
|
||||
---
|
||||
request_serializer: JoinGroupRequestSerializer
|
||||
"""
|
||||
serializer = JoinGroupRequestSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
data = serializer.data
|
||||
try:
|
||||
group = Group.objects.get(id=data["group_id"])
|
||||
except Group.DesoNotExist:
|
||||
return error_response(u"小组不存在")
|
||||
if group.join_group_setting == 0:
|
||||
join_group(request.user, group)
|
||||
return success_response(u"你已经成功的加入该小组")
|
||||
elif group.join_group_setting == 1:
|
||||
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)
|
||||
|
||||
0
install/__init__.py
Normal file
0
install/__init__.py
Normal file
3
install/admin.py
Normal file
3
install/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
0
install/migrations/__init__.py
Normal file
0
install/migrations/__init__.py
Normal file
3
install/models.py
Normal file
3
install/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
3
install/tests.py
Normal file
3
install/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
12
install/views.py
Normal file
12
install/views.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# coding=utf-8
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponse
|
||||
|
||||
from account.models import User
|
||||
|
||||
|
||||
def install(request):
|
||||
user = User.objects.create(username="root", admin_type=2)
|
||||
user.set_password("root")
|
||||
user.save()
|
||||
return HttpResponse("success")
|
||||
@@ -9,8 +9,11 @@ from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterA
|
||||
from announcement.views import AnnouncementAPIView, AnnouncementAdminAPIView
|
||||
from group.views import GroupAdminAPIView
|
||||
from admin.views import AdminTemplateView
|
||||
from problem.views import TestCaseUploadAPIView
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^install/$', "install.views.install"),
|
||||
url("^$", TemplateView.as_view(template_name="oj/index.html"), name="index_page"),
|
||||
url(r'^docs/', include('rest_framework_swagger.urls')),
|
||||
url(r'^admin/$', TemplateView.as_view(template_name="admin/admin.html"), name="admin_spa_page"),
|
||||
@@ -27,10 +30,12 @@ urlpatterns = [
|
||||
url(r'^problem/(?P<problem_id>\d+)/$', "problem.views.problem_page", name="problem_page"),
|
||||
|
||||
url(r'^api/announcements/$', AnnouncementAPIView.as_view(), name="announcement_list_api"),
|
||||
url(r'^api/users/$', UserAPIView.as_view(), name="user_list_api"),
|
||||
url(r'^api/admin/users/$', UserAPIView.as_view(), name="user_list_api"),
|
||||
|
||||
url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), name="add_contest_page"),
|
||||
url(r'^problems/$', TemplateView.as_view(template_name="oj/problem/problem_list.html"), name="problem_list_page"),
|
||||
url(r'^admin/template/(?P<template_dir>\w+)/(?P<template_name>\w+).html', AdminTemplateView.as_view(), name="admin_template"),
|
||||
url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"),
|
||||
|
||||
url(r'^api/admin/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_api"),
|
||||
]
|
||||
|
||||
@@ -1,7 +1,98 @@
|
||||
# coding=utf-8
|
||||
import zipfile
|
||||
import re
|
||||
import os
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
from django.shortcuts import render
|
||||
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from utils.shortcuts import rand_str, error_response, success_response
|
||||
|
||||
|
||||
def problem_page(request, problem_id):
|
||||
# todo
|
||||
return render(request, "oj/problem/problem.html")
|
||||
|
||||
|
||||
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 = "test_case/" + 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))
|
||||
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"测试用例压缩文件格式错误,请保证测试用例文件在根目录下直接压缩")
|
||||
|
||||
@@ -9,8 +9,15 @@ define("csrf",function(){
|
||||
}
|
||||
return "";
|
||||
}
|
||||
function csrfHeader(xhr){
|
||||
xhr.setRequestHeader("X-CSRFToken", get_cookie("csrftoken"));
|
||||
function csrfHeader(){
|
||||
// jquery的请求
|
||||
if(arguments.length == 1) {
|
||||
arguments[0].setRequestHeader("X-CSRFToken", get_cookie("csrftoken"));
|
||||
}
|
||||
// 百度webuploader 的请求
|
||||
else if(arguments.length == 3){
|
||||
arguments[2]["X-CSRFToken"] = get_cookie("csrftoken");
|
||||
}
|
||||
}
|
||||
return csrfHeader;
|
||||
});
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
# coding=utf-8
|
||||
import hashlib
|
||||
import time
|
||||
import random
|
||||
|
||||
from django.core.paginator import Paginator
|
||||
|
||||
from rest_framework.response import Response
|
||||
@@ -87,3 +91,8 @@ def paginate(request, query_set, object_serializer):
|
||||
pass
|
||||
|
||||
return success_response(data)
|
||||
|
||||
|
||||
def rand_str(length=32):
|
||||
string = hashlib.md5(str(time.time()) + str(random.randrange(1, 9999999900))).hexdigest()
|
||||
return string[0:length]
|
||||
Reference in New Issue
Block a user