From 2ce38a6fc020313bd2b20ab97d67e1e1e21392fa Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 9 Aug 2015 18:00:52 +0800 Subject: [PATCH 01/15] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86AdminRequired?= =?UTF-8?q?=E7=9A=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/middleware.py | 2 +- admin/tests.py | 68 +++++++++++++++++++++++++++++++++++++++++++++ oj/settings.py | 2 +- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/admin/middleware.py b/admin/middleware.py index 515a889..ccb689b 100644 --- a/admin/middleware.py +++ b/admin/middleware.py @@ -8,7 +8,7 @@ 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(): + 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") diff --git a/admin/tests.py b/admin/tests.py index e69de29..8c88c3b 100644 --- a/admin/tests.py +++ b/admin/tests.py @@ -0,0 +1,68 @@ +# 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 AdminRequiredMidlewareTest(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") + rresponse = 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) + diff --git a/oj/settings.py b/oj/settings.py index c8a07ec..469dfe4 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -39,7 +39,6 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = ( - 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', @@ -50,6 +49,7 @@ INSTALLED_APPS = ( 'announcement', 'utils', 'group', + 'admin', 'rest_framework', 'rest_framework_swagger', From a9b9ae7f767ed03f3b3a0bf27eb32f07809b59fd Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 9 Aug 2015 19:06:55 +0800 Subject: [PATCH 02/15] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E5=B0=8F=E7=BB=84=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group/migrations/0001_initial.py | 18 +++++++++++++++- group/models.py | 11 +++++++++- group/serializers.py | 2 +- group/views.py | 35 ++++++++++++++++++++++++++++++-- 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/group/migrations/0001_initial.py b/group/migrations/0001_initial.py index f996a63..68da3ae 100644 --- a/group/migrations/0001_initial.py +++ b/group/migrations/0001_initial.py @@ -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'), + ), ] diff --git a/group/models.py b/group/models.py index aa255f4..0508dc0 100644 --- a/group/models.py +++ b/group/models.py @@ -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") diff --git a/group/serializers.py b/group/serializers.py index bd655ed..3bc88d4 100644 --- a/group/serializers.py +++ b/group/serializers.py @@ -24,4 +24,4 @@ class JoinGroupRequestSerializer(serializers.Serializer): class GroupSerializer(serializers.ModelSerializer): class Meta: model = Group - exclude = ["members"] \ No newline at end of file + exclude = ["members"] diff --git a/group/views.py b/group/views.py index 5ccdcd9..65499f1 100644 --- a/group/views.py +++ b/group/views.py @@ -5,8 +5,9 @@ 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) @@ -53,6 +54,8 @@ class GroupAdminAPIView(APIView): def get(self, request): """ 查询小组列表或者单个小组的信息 + --- + response_serializer: GroupSerializer """ group_id = request.GET.get("group_id", None) if group_id: @@ -69,4 +72,32 @@ class GroupAdminAPIView(APIView): groups = Group.objects.filter(visible=True) else: groups = Group.objects.filter(admin=request.user, visible=True) - return paginate(request, groups, GroupSerializer) \ No newline at end of file + return paginate(request, groups, GroupSerializer) + + +def join_group(user, group): + return UserGroupRelation.objects.create(user=user, group=group) + + +class JoinGroupAPIView(APIView): + @login_required + def post(self, request): + serializer = JoinGroupRequestSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + try: + group = Grouo.objects.get(id=data["group"]) + 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): + pass \ No newline at end of file From 06528d379ac970affe930d9e5102669306db059e Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 9 Aug 2015 19:13:48 +0800 Subject: [PATCH 03/15] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E5=B0=8F=E7=BB=84=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group/serializers.py | 2 +- group/views.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/group/serializers.py b/group/serializers.py index 3bc88d4..e8f83e3 100644 --- a/group/serializers.py +++ b/group/serializers.py @@ -17,7 +17,7 @@ class EditGroupSerializer(serializers.Serializer): class JoinGroupRequestSerializer(serializers.Serializer): - group = serializers.IntegerField() + group_id = serializers.IntegerField() message = serializers.CharField(max_length=30) diff --git a/group/views.py b/group/views.py index 65499f1..461b665 100644 --- a/group/views.py +++ b/group/views.py @@ -86,7 +86,7 @@ class JoinGroupAPIView(APIView): if serializer.is_valid(): data = serializer.data try: - group = Grouo.objects.get(id=data["group"]) + group = Grouo.objects.get(id=data["group_id"]) except Group.DesoNotExist: return error_response(u"小组不存在") if group.join_group_setting == 0: @@ -100,4 +100,8 @@ class JoinGroupAPIView(APIView): return serializer_invalid_response(serializer) def get(self, request): - pass \ No newline at end of file + 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) From c772a4bcdca4a015c9b6293e75fcb97da7c8d655 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 9 Aug 2015 19:59:25 +0800 Subject: [PATCH 04/15] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=B0=8F=E7=BB=84?= =?UTF-8?q?=E6=88=90=E5=91=98=E7=AE=A1=E7=90=86=E7=9A=84api=EF=BC=9B?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9D=83=E9=99=90=E5=88=A4=E6=96=AD=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=92=8C=E7=B2=BE=E7=AE=80=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group/serializers.py | 16 ++++++++++- group/views.py | 67 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/group/serializers.py b/group/serializers.py index e8f83e3..9718fed 100644 --- a/group/serializers.py +++ b/group/serializers.py @@ -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): @@ -25,3 +26,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()) \ No newline at end of file diff --git a/group/views.py b/group/views.py index 461b665..7b9677b 100644 --- a/group/views.py +++ b/group/views.py @@ -9,10 +9,34 @@ from account.decorators import login_required 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(request, group_id): + """ + 根据group_id查询指定的小组的信息,结合判断用户权限 + 管理员可以查询所有的小组,其他用户查询自己创建的自傲组 + """ + if request.user.admin_type == SUPER_ADMIN: + group = Group.object.get(id=group_id, visible=True) + else: + group = Group.object.get(id=group_id, visible=True, admin=request.user) + return group + + def get_groups(request): + """ + 如果是超级管理员,就返回全部的小组 + 如果是管理员,就返回他创建的全部小组 + """ + if request.user.admin_type == SUPER_ADMIN: + groups = Group.objects.filter(visible=True) + else: + groups = Group.objects.filter(admin=request.user, visible=True) + + +class GroupAdminAPIView(APIView, GroupAPIViewBase): def post(self, request): """ 创建小组的api @@ -40,7 +64,7 @@ class GroupAdminAPIView(APIView): 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"] @@ -60,21 +84,41 @@ class GroupAdminAPIView(APIView): 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): + 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): + serializer = EditGroupMemberSerializer(data=request.data) + if serializer.is_valid(): + try: + group = self.get_group(request, group_id) + except Group.DoesNotExist: + return error_response(u"小组不存在") + user_id_list = serializer.data["members"] + UserGroupRelation.objects.delete(group=group, user__id__in=user_id_list) + return success_response(u"删除成功") + else: + return serializer_invalid_response(serializer) + + def join_group(user, group): return UserGroupRelation.objects.create(user=user, group=group) @@ -103,5 +147,6 @@ class JoinGroupAPIView(APIView): 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) From a11e935119a5a40622efb2861b19c7d68a667d03 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 9 Aug 2015 20:05:10 +0800 Subject: [PATCH 05/15] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=B0=8F=E7=BB=84?= =?UTF-8?q?=E6=9C=89=E5=85=B3api=E7=9A=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group/views.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/group/views.py b/group/views.py index 7b9677b..fb19086 100644 --- a/group/views.py +++ b/group/views.py @@ -42,6 +42,7 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase): 创建小组的api --- request_serializer: CreateGroupSerializer + response_serializer: GroupSerializer """ serializer = CreateGroupSerializer(data=request.data) if serializer.is_valid(): @@ -59,6 +60,7 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase): 修改小组信息的api --- request_serializer: EditGroupSerializer + response_serializer: GroupSerializer """ serializer = EditGroupSerializer(data=request.data) if serializer.is_valid(): @@ -77,7 +79,7 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase): def get(self, request): """ - 查询小组列表或者单个小组的信息 + 查询小组列表或者单个小组的信息,查询单个小组需要传递group_id参数,否则返回全部 --- response_serializer: GroupSerializer """ @@ -95,6 +97,11 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase): 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"参数错误") @@ -106,6 +113,11 @@ class GroupMemberAdminAPIView(APIView, GroupAPIViewBase): 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: @@ -126,6 +138,11 @@ def join_group(user, 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 @@ -144,6 +161,11 @@ class JoinGroupAPIView(APIView): 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"参数错误") From da625222796ee2718c2767974d808e8b6b172621 Mon Sep 17 00:00:00 2001 From: sxw Date: Sun, 9 Aug 2015 20:50:04 +0800 Subject: [PATCH 06/15] =?UTF-8?q?[=E5=89=8D=E7=AB=AF-=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=A2=98=E7=9B=AE=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=AF=94=E8=B5=9B?= =?UTF-8?q?=20]:=201.=E6=96=B0=E5=A2=9E=E6=B7=BB=E5=8A=A0=E9=A2=98?= =?UTF-8?q?=E7=9B=AE=E9=A1=B5=E9=9D=A2=E3=80=82=202.=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=AF=94=E8=B5=9B=E9=A1=B5=E9=9D=A2=EF=BC=8C?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=BA=86avalon=E7=9A=84=E7=94=A8=E6=B3=95?= =?UTF-8?q?=EF=BC=8C=E6=96=B0=E5=A2=9E=E5=AF=8C=E6=96=87=E6=9C=AC=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E6=A1=86=E7=9A=84=E9=9D=9E=E7=A9=BA=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=203.=E6=94=B6=E9=9B=86=E6=96=B0=E5=A2=9E=E6=AF=94=E8=B5=9B?= =?UTF-8?q?=E5=92=8C=E6=96=B0=E5=A2=9E=E9=A2=98=E7=9B=AE=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E8=BE=93=E5=85=A5=EF=BC=8C=E6=8F=90=E4=BA=A4=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E5=90=8E=E5=8F=AF=E4=BB=A5=E5=9C=A8console=E4=B8=AD=E7=9C=8B?= =?UTF-8?q?=E5=88=B0=E6=8F=90=E4=BA=A4=E7=9A=84=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [CI SKIP] --- static/src/js/app/admin/contest/contest.js | 64 ++++++----- .../src/js/app/admin/problem/add_problem.js | 108 ++++++++++++++++++ .../{img => js/lib/webuploader}/Uploader.swf | Bin static/src/js/utils/uploader.js | 4 +- template/admin/contest/add_contest.html | 78 +++++++------ template/admin/problem/add_problem.html | 78 +++++++++++++ 6 files changed, 268 insertions(+), 64 deletions(-) create mode 100644 static/src/js/app/admin/problem/add_problem.js rename static/src/{img => js/lib/webuploader}/Uploader.swf (100%) create mode 100644 template/admin/problem/add_problem.html diff --git a/static/src/js/app/admin/contest/contest.js b/static/src/js/app/admin/contest/contest.js index 6cd531d..e9b44ba 100644 --- a/static/src/js/app/admin/contest/contest.js +++ b/static/src/js/app/admin/contest/contest.js @@ -19,6 +19,13 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker", } } }, + description:{ + validators: { + notEmpty: { + message: "请输入描述" + } + } + }, start_time: { validators: { notEmpty: { @@ -93,9 +100,18 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker", }) .on("success.form.fv", function (e) { e.preventDefault(); - alert("1111"); + var data = {title: vm.title, description: vm.description, start_time: vm.startTime, end_time: vm.endTime, + password: vm.password, model: vm.model, open_rank: vm.openRank, problems:[]}; + for (var i = 0; i < vm.problems.length; i++) { + var problem = {title: vm.problems[i].title, description:vm.problems[i].description, + cpu:vm.problems[i].cpu, memory:vm.problems[i].memory,samples:[]}; + for (var j = 0; j < vm.problems[i].samples.length; j++) { + problem.samples.push({input:vm.problems[i].samples[j].input, output:vm.problems[i].samples[j].output}) + } + data.problems.push(problem); + } + console.log(data); }); - function make_id() { var text = ""; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; @@ -107,17 +123,20 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker", var vm = avalon.define({ $id: "add_contest", + title : "", + description: "", + startTime: "", + endTime: "", + password: "", + model: "", + openRank: false, problems: [], add_problem: function () { - var problem = {}; var problem_id = make_id(); - problem["id"] = problem_id; - problem["samples"] = []; - problem["webuploader"] = {}; - problem["toggle_string"] = "折叠"; + var problem={id: problem_id, title: "", cpu: "", memory: "", description: "",samples: [], webuploader: {}, visible: true}; vm.problems.push(problem); - uploader("#problem-" + problem_id + "-uploader"); - console.log(vm.problems); + uploader("#problem-" + problem_id + "-uploader",""); + editor("#problem-" + problem_id + "-description") $("#add-contest-form").formValidation('addField', $('[name="problem_name[]"]')); $("#add-contest-form").formValidation('addField', $('[name="cpu[]"]')); $("#add-contest-form").formValidation('addField', $('[name="memory[]"]')); @@ -127,31 +146,21 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker", vm.problems.remove(problem); } }, - toggle_problem: function (problem) { - $("#" + "problem-" + problem.id + "-body").toggle(); - if (problem["toggle_string"] == "展开") { - problem["toggle_string"] = "折叠"; - } - else { - problem["toggle_string"] = "展开"; - } + toggle: function (item) { + item.visible = !item.visible; }, add_sample: function (problem) { - problem["samples"].push({"id": make_id(), "toggle_string": "折叠"}); + problem.samples.push({id: make_id(), visible: true, input: "", output: ""}); }, del_sample: function (problem, sample) { if (confirm("你确定要删除么?")) { - problem["samples"].remove(sample); + problem.samples.remove(sample); } }, - toggle_sample: function (problem, sample) { - $("#" + "problem-" + problem.id + "-sampleio-" + sample.id + "-body").toggle(); - if (sample["toggle_string"] == "展开") { - sample["toggle_string"] = "折叠"; - } - else { - sample["toggle_string"] = "展开"; - } + getBtnContent: function (item) { + if (item.visible) + return "折叠"; + return "展开"; } }); avalon.scan(); @@ -168,7 +177,6 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker", weekStart: 1, language: "zh-CN" }); - $("#contest_start_time").datetimepicker() .on("hide", function (ev) { $("#add-contest-form") diff --git a/static/src/js/app/admin/problem/add_problem.js b/static/src/js/app/admin/problem/add_problem.js new file mode 100644 index 0000000..c60fd23 --- /dev/null +++ b/static/src/js/app/admin/problem/add_problem.js @@ -0,0 +1,108 @@ +require(["jquery", "avalon", "editor", "uploader", "validation"], + function ($, avalon, editor, uploader) { + avalon.vmodels.add_problem = null; + $("#add-problem-form") + .formValidation({ + framework: "bootstrap", + fields: { + title: { + validators: { + notEmpty: { + message: "请填写题目名称" + }, + stringLength: { + min: 1, + max: 30, + message: "名称不能超过30个字" + } + } + }, + description:{ + validators: { + notEmpty: { + message: "请输入描述" + } + } + }, + cpu: { + validators: { + notEmpty: { + message: "请输入cpu时间" + }, + integer: { + message: "请输入一个合法的数字" + }, + between: { + inclusive: true, + min: 1, + max: 5000, + message: "只能在1-5000之间" + } + } + }, + memory: { + validators: { + notEmpty: { + message: "请输入内存" + }, + integer: { + message: "请输入一个合法的数字" + } + } + } + } + }) + .on("success.form.fv", function (e) { + e.preventDefault(); + var ajaxData = { + title: vm.title, + description: vm.description, + cpu: vm.cpu, + memory: vm.memory, + samples: [] + }; + + for (var i = 0; i < vm.samples.length; i++) { + ajaxData.samples.push({input: vm.samples[i].input, output: vm.samples[i].output}); + } + console.log(ajaxData); + }); + var problemDiscription = editor("#problemDescription"); + var testCaseUploader = uploader("#testCaseFile", "/admin/api/testCase");//{ + + /*auto: true, + swf: '/static/js/lib/webuploader/Uploader.swf', + server: 'http://webuploader.duapp.com/server/fileupload.php', + multiple:false, + accept: { + title: 'Zip', + extensions: 'zip', + mimeTypes: 'zip/*' + }*/ + // }); + var vm = avalon.define({ + $id: "add_problem", + title: "", + description: "", + cpu: 0, + memory: 0, + samples: [], + add_sample: function () { + vm.samples.push({input: "", output: "", "visible": true}); + }, + del_sample: function (sample) { + if (confirm("你确定要删除么?")) { + vm.samples.remove(sample); + } + }, + toggle_sample: function (sample) { + sample.visible = !sample.visible; + }, + getBtnContent: function (item) { + if (item.visible) + return "折叠"; + return "展开"; + } + }); + avalon.scan(); + }); \ No newline at end of file diff --git a/static/src/img/Uploader.swf b/static/src/js/lib/webuploader/Uploader.swf similarity index 100% rename from static/src/img/Uploader.swf rename to static/src/js/lib/webuploader/Uploader.swf diff --git a/static/src/js/utils/uploader.js b/static/src/js/utils/uploader.js index 76e5447..ec418c5 100644 --- a/static/src/js/utils/uploader.js +++ b/static/src/js/utils/uploader.js @@ -1,12 +1,12 @@ define("uploader", ["webuploader"], function(webuploader){ - function uploader(selector) { + function uploader(selector, server) { return webuploader.create({ // swf文件路径 swf: "/js/Uploader.swf", // 文件接收服务端。 - server: "http://webuploader.duapp.com/server/fileupload.php", + server: server, // 选择文件的按钮。可选。 // 内部根据当前运行是创建,可能是input元素,也可能是flash. diff --git a/template/admin/contest/add_contest.html b/template/admin/contest/add_contest.html index 4ac0be8..6349814 100644 --- a/template/admin/contest/add_contest.html +++ b/template/admin/contest/add_contest.html @@ -2,11 +2,11 @@
- +
- +
@@ -14,7 +14,8 @@
- + + 请填写比赛描述
@@ -25,12 +26,13 @@
- +
- +
@@ -51,18 +53,24 @@
- +
- OI - ACM + +
- 开放排名 +
@@ -75,23 +83,22 @@
-
+
- +
@@ -102,14 +109,18 @@
- +
- +
+ + + 请填写题目描述 -
-
- - -
-
+
+
- + +
-
- -
-
+
- + +
@@ -163,14 +169,18 @@
选择文件
-
+
+
+ +
+
diff --git a/template/admin/problem/add_problem.html b/template/admin/problem/add_problem.html new file mode 100644 index 0000000..1da56a9 --- /dev/null +++ b/template/admin/problem/add_problem.html @@ -0,0 +1,78 @@ +
+
+
+
+ + +
+
+ + + 请填写题目描述 +
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+ + 添加 + +
+
+ +
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+
+ +
+
+
+
选择文件
+
+
+
+
+ +
+
+
+ + \ No newline at end of file From f938950de2050776e23ab5bb92de875a7a52d62f Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 9 Aug 2015 21:00:16 +0800 Subject: [PATCH 07/15] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=88=97=E8=A1=A8url=E7=BC=BA=E5=B0=91admin=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oj/urls.py b/oj/urls.py index b2eecb9..ef840e0 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -27,7 +27,7 @@ urlpatterns = [ url(r'^problem/(?P\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"), From a90fdd3f1d34ddbd33242bc5784fbe3311fa5376 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 9 Aug 2015 22:05:22 +0800 Subject: [PATCH 08/15] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AF=AD=E6=B3=95?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/group/views.py b/group/views.py index fb19086..6119cc4 100644 --- a/group/views.py +++ b/group/views.py @@ -14,7 +14,7 @@ from .serializers import (CreateGroupSerializer, EditGroupSerializer, class GroupAPIViewBase(object): - def get_group(request, group_id): + def get_group(self, request, group_id): """ 根据group_id查询指定的小组的信息,结合判断用户权限 管理员可以查询所有的小组,其他用户查询自己创建的自傲组 @@ -25,7 +25,7 @@ class GroupAPIViewBase(object): group = Group.object.get(id=group_id, visible=True, admin=request.user) return group - def get_groups(request): + def get_groups(self, request): """ 如果是超级管理员,就返回全部的小组 如果是管理员,就返回他创建的全部小组 From b6c31ba7a63a7a35e7ad6ce5fd41d482d0494586 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 9 Aug 2015 22:06:23 +0800 Subject: [PATCH 09/15] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install/__init__.py | 0 install/admin.py | 3 +++ install/migrations/__init__.py | 0 install/models.py | 3 +++ install/tests.py | 3 +++ install/views.py | 12 ++++++++++++ oj/urls.py | 1 + 7 files changed, 22 insertions(+) create mode 100644 install/__init__.py create mode 100644 install/admin.py create mode 100644 install/migrations/__init__.py create mode 100644 install/models.py create mode 100644 install/tests.py create mode 100644 install/views.py diff --git a/install/__init__.py b/install/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/install/admin.py b/install/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/install/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/install/migrations/__init__.py b/install/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/install/models.py b/install/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/install/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/install/tests.py b/install/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/install/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/install/views.py b/install/views.py new file mode 100644 index 0000000..521d8a6 --- /dev/null +++ b/install/views.py @@ -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") \ No newline at end of file diff --git a/oj/urls.py b/oj/urls.py index ef840e0..45d4b80 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -11,6 +11,7 @@ from group.views import GroupAdminAPIView from admin.views import AdminTemplateView 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"), From 09b59d6e43cf00dde6077459e7014d096b3f8e15 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 10 Aug 2015 12:24:36 +0800 Subject: [PATCH 10/15] fix typo --- group/serializers.py | 1 + group/views.py | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/group/serializers.py b/group/serializers.py index 9718fed..6c40eae 100644 --- a/group/serializers.py +++ b/group/serializers.py @@ -12,6 +12,7 @@ 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() diff --git a/group/views.py b/group/views.py index 6119cc4..cd8ac54 100644 --- a/group/views.py +++ b/group/views.py @@ -20,9 +20,9 @@ class GroupAPIViewBase(object): 管理员可以查询所有的小组,其他用户查询自己创建的自傲组 """ if request.user.admin_type == SUPER_ADMIN: - group = Group.object.get(id=group_id, visible=True) + group = Group.objects.get(id=group_id, visible=True) else: - group = Group.object.get(id=group_id, visible=True, admin=request.user) + group = Group.objects.get(id=group_id, visible=True, admin=request.user) return group def get_groups(self, request): @@ -34,6 +34,7 @@ class GroupAPIViewBase(object): groups = Group.objects.filter(visible=True) else: groups = Group.objects.filter(admin=request.user, visible=True) + return groups class GroupAdminAPIView(APIView, GroupAPIViewBase): @@ -121,11 +122,11 @@ class GroupMemberAdminAPIView(APIView, GroupAPIViewBase): serializer = EditGroupMemberSerializer(data=request.data) if serializer.is_valid(): try: - group = self.get_group(request, group_id) + group = self.get_group(request, serializer.data["group_id"]) except Group.DoesNotExist: return error_response(u"小组不存在") user_id_list = serializer.data["members"] - UserGroupRelation.objects.delete(group=group, user__id__in=user_id_list) + UserGroupRelation.objects.filter(group=group, user__id__in=user_id_list).delete() return success_response(u"删除成功") else: return serializer_invalid_response(serializer) @@ -147,7 +148,7 @@ class JoinGroupAPIView(APIView): if serializer.is_valid(): data = serializer.data try: - group = Grouo.objects.get(id=data["group_id"]) + group = Group.objects.get(id=data["group_id"]) except Group.DesoNotExist: return error_response(u"小组不存在") if group.join_group_setting == 0: From c809d0de479e5c3f556fd650c727644104113b63 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 10 Aug 2015 12:25:35 +0800 Subject: [PATCH 11/15] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E5=92=8C=E8=A7=A3=E6=9E=90=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= =?UTF-8?q?=E5=8E=8B=E7=BC=A9=E5=8C=85=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 4 +++ problem/views.py | 66 ++++++++++++++++++++++++++++++++++++++++++++++ utils/shortcuts.py | 9 +++++++ 3 files changed, 79 insertions(+) diff --git a/oj/urls.py b/oj/urls.py index 45d4b80..bcfb211 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -9,6 +9,8 @@ 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"), @@ -34,4 +36,6 @@ urlpatterns = [ url(r'^problems/$', TemplateView.as_view(template_name="oj/problem/problem_list.html"), name="problem_list_page"), url(r'^admin/template/(?P\w+)/(?P\w+).html', AdminTemplateView.as_view(), name="admin_template"), url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"), + + url(r'^api/admin/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_api"), ] diff --git a/problem/views.py b/problem/views.py index 58fb9e6..bf5927e 100644 --- a/problem/views.py +++ b/problem/views.py @@ -1,7 +1,73 @@ # coding=utf-8 +import zipfile +import re from django.shortcuts import render +from django.views.decorators.csrf import csrf_exempt + +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 + + @csrf_exempt + def post(self, request): + f = request.FILES["file"] + + tmp_zip = "tmp/" + rand_str() + ".zip" + with open(tmp_zip) 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.spit(".") + # 有了.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 + "/" + + # 得到了合法的测试用例文件列表 然后去解压缩 + for name in l: + f = open(test_case_dir + name, "w+b") + f.write(test_case_file.read(name)) + f.close() + + return success_response(problem_test_dir) + + else: + return error_response(u"测试用例压缩文件格式错误,请保证测试用例文件在根目录下直接压缩") + diff --git a/utils/shortcuts.py b/utils/shortcuts.py index 44610a9..a254346 100644 --- a/utils/shortcuts.py +++ b/utils/shortcuts.py @@ -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] \ No newline at end of file From d58dd821aa57094ae79ed1f0ac804eb6dfba819d Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 10 Aug 2015 13:45:56 +0800 Subject: [PATCH 12/15] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E6=98=AF=20jQuery=20=E7=9A=84=20Ajax=20=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E8=BF=98=E6=98=AF=20webuploader=20=E7=9A=84=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E8=AF=B7=E6=B1=82=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=B8=8D=E5=90=8C=E7=9A=84=20csrf=20token.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/utils/csrf.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/static/src/js/utils/csrf.js b/static/src/js/utils/csrf.js index 767542b..6a004b4 100644 --- a/static/src/js/utils/csrf.js +++ b/static/src/js/utils/csrf.js @@ -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; }); From 17de985404b6c3450bff193f511e116edba4c244 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 10 Aug 2015 13:53:03 +0800 Subject: [PATCH 13/15] =?UTF-8?q?fix=20typo=20=EF=BC=9B=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E4=B8=8D=E5=AD=98=E5=9C=A8=E7=9A=84=E6=96=87=E4=BB=B6=E5=A4=B9?= =?UTF-8?q?=20=EF=BC=9B=E5=8A=A0=E5=BC=BA=E6=9C=89=E6=95=88=E6=80=A7?= =?UTF-8?q?=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problem/views.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/problem/views.py b/problem/views.py index bf5927e..08d096d 100644 --- a/problem/views.py +++ b/problem/views.py @@ -1,8 +1,9 @@ # coding=utf-8 import zipfile import re +import os + from django.shortcuts import render -from django.views.decorators.csrf import csrf_exempt from rest_framework.views import APIView @@ -15,18 +16,19 @@ def problem_page(request, problem_id): 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 - @csrf_exempt 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) as test_case_zip: + with open(tmp_zip, "wb") as test_case_zip: for chunk in f: test_case_zip.write(chunk) @@ -43,7 +45,7 @@ class TestCaseUploadAPIView(APIView): 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.spit(".") + name = file_name.split(".") # 有了.in 判断对应的.out 在不在 if name[1] == "in": if (name[0] + ".out") in name_list: @@ -61,13 +63,14 @@ class TestCaseUploadAPIView(APIView): test_case_dir = "test_case/" + problem_test_dir + "/" # 得到了合法的测试用例文件列表 然后去解压缩 + os.mkdir(test_case_dir) for name in l: - f = open(test_case_dir + name, "w+b") + f = open(test_case_dir + name, "wb") f.write(test_case_file.read(name)) f.close() - - return success_response(problem_test_dir) - + l.sort() + return success_response({"test_case_id": problem_test_dir, + "file_list": {"input": l[0::2], + "output": l[1::2]}}) else: return error_response(u"测试用例压缩文件格式错误,请保证测试用例文件在根目录下直接压缩") - From 63bcb09c0f2f284dc216a3d5454d3d145ae5af4b Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 10 Aug 2015 13:54:19 +0800 Subject: [PATCH 14/15] =?UTF-8?q?=E6=8E=92=E9=99=A4=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=E4=B8=8A=E4=BC=A0=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 12a8c85..73adda5 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,6 @@ db.db db.sqlite3 .DS_Store log/ -release/ \ No newline at end of file +release/ +tmp/ +test_case/ \ No newline at end of file From 526b2d0f6e1c1f96326d80b25cf667549e6dc0b9 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 10 Aug 2015 14:30:06 +0800 Subject: [PATCH 15/15] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=90=8E=E5=86=99=E5=85=A5=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problem/views.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/problem/views.py b/problem/views.py index 08d096d..0d9d2b1 100644 --- a/problem/views.py +++ b/problem/views.py @@ -2,6 +2,8 @@ import zipfile import re import os +import hashlib +import json from django.shortcuts import render @@ -69,6 +71,26 @@ class TestCaseUploadAPIView(APIView): 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]}})