diff --git a/.gitignore b/.gitignore index 12a8c859..73adda50 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 diff --git a/admin/middleware.py b/admin/middleware.py index 515a8893..ccb689bc 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 e69de29b..8c88c3bc 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/group/migrations/0001_initial.py b/group/migrations/0001_initial.py index f996a63d..68da3ae6 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 aa255f41..0508dc03 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 bd655ede..6c40eaec 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): @@ -11,17 +12,31 @@ 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) class GroupSerializer(serializers.ModelSerializer): class Meta: model = Group - exclude = ["members"] \ No newline at end of file + 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 5ccdcd93..cd8ac54f 100644 --- a/group/views.py +++ b/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) - return paginate(request, groups, GroupSerializer) \ No newline at end of file + 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) diff --git a/install/__init__.py b/install/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/install/admin.py b/install/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /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 00000000..e69de29b diff --git a/install/models.py b/install/models.py new file mode 100644 index 00000000..71a83623 --- /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 00000000..7ce503c2 --- /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 00000000..521d8a6c --- /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/settings.py b/oj/settings.py index c8a07ec2..469dfe4e 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', diff --git a/oj/urls.py b/oj/urls.py index b2eecb9e..bcfb2110 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -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\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\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 58fb9e6d..0d9d2b14 100644 --- a/problem/views.py +++ b/problem/views.py @@ -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"测试用例压缩文件格式错误,请保证测试用例文件在根目录下直接压缩") diff --git a/static/src/js/app/admin/contest/contest.js b/static/src/js/app/admin/contest/contest.js index 6cd531d9..e9b44bac 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 00000000..c60fd23d --- /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/csrf.js b/static/src/js/utils/csrf.js index 767542bb..6a004b4b 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; }); diff --git a/static/src/js/utils/uploader.js b/static/src/js/utils/uploader.js index 76e54477..ec418c56 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 4ac0be84..63498141 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 00000000..1da56a91 --- /dev/null +++ b/template/admin/problem/add_problem.html @@ -0,0 +1,78 @@ +
+
+
+
+ + +
+
+ + + 请填写题目描述 +
+
+ +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+ + 添加 + +
+
+ +
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+
+ +
+
+
+
选择文件
+
+
+
+
+ +
+
+
+ + \ No newline at end of file diff --git a/utils/shortcuts.py b/utils/shortcuts.py index 44610a9f..a2543461 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