From 616aed6a5c445ac03c5556c778d54fd5f38b05b6 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 17 Aug 2015 20:56:42 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A2=98=E7=9B=AE?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E6=80=BB=E6=95=B0=E5=92=8C=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E7=8E=87=E7=BB=9F=E8=AE=A1=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/db_router.py | 5 ++--- .../migrations/0002_submission_is_counted.py | 19 +++++++++++++++++++ submission/models.py | 1 + submission/views.py | 15 +++++++++++++++ template/oj/problem/problem_list.html | 2 +- 5 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 submission/migrations/0002_submission_is_counted.py diff --git a/oj/db_router.py b/oj/db_router.py index d8c1659b..3a20427c 100644 --- a/oj/db_router.py +++ b/oj/db_router.py @@ -17,7 +17,6 @@ class DBRouter(object): def allow_migrate(self, db, app_label, model=None, **hints): if app_label == "submission": - if db == "submission": - return db == app_label + return db == app_label else: - return db == "default" \ No newline at end of file + return db == "default" diff --git a/submission/migrations/0002_submission_is_counted.py b/submission/migrations/0002_submission_is_counted.py new file mode 100644 index 00000000..169a9a36 --- /dev/null +++ b/submission/migrations/0002_submission_is_counted.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('submission', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='submission', + name='is_counted', + field=models.BooleanField(default=False), + ), + ] diff --git a/submission/models.py b/submission/models.py index a2fcfde8..3442183d 100644 --- a/submission/models.py +++ b/submission/models.py @@ -18,6 +18,7 @@ class Submission(models.Model): accepted_answer_time = models.IntegerField(blank=True, null=True) # 这个字段只有在题目是accepted 的时候才会用到,比赛题目的提交可能还会有得分等信息,存储在这里面 accepted_answer_info = models.TextField(blank=True, null=True) + is_counted = models.BooleanField(default=False) class Meta: db_table = "submission" diff --git a/submission/views.py b/submission/views.py index 10c77867..f8c631bf 100644 --- a/submission/views.py +++ b/submission/views.py @@ -28,6 +28,9 @@ class SubmissionAPIView(APIView): data = serializer.data try: problem = Problem.objects.get(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"]), @@ -51,6 +54,18 @@ class SubmissionAPIView(APIView): submission = Submission.objects.get(id=submission_id, user_id=request.user.id) except Submission.DoesNotExist: return error_response(u"提交不存在") + # 标记这个submission 已经被统计 + if not submission.is_counted: + submission.is_counted = True + submission.save() + if submission.result == result["accepted"]: + # 更新题目的 ac 计数器 + try: + problem = Problem.objects.get(id=submission.problem_id) + problem.total_accepted_number += 1 + problem.save() + except Problem.DoesNotExist: + pass response_data = {"result": submission.result} if submission.result == 0: response_data["accepted_answer_time"] = submission.accepted_answer_time diff --git a/template/oj/problem/problem_list.html b/template/oj/problem/problem_list.html index 53844de3..012f5d27 100644 --- a/template/oj/problem/problem_list.html +++ b/template/oj/problem/problem_list.html @@ -30,7 +30,7 @@ {{ item.id }} {{ item.title }} {{ item.difficulty }} - {{ item|accepted_radio }} + {{ item|accepted_radio }}% {% endfor %} From 016a49a53b766e5c903f11cf48ca32635b018cf5 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 18 Aug 2015 16:03:25 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=99=BB=E9=99=86?= =?UTF-8?q?=E5=90=8E=E8=B7=B3=E8=BD=AC=E5=9B=9E=E6=9D=A5=E6=BA=90=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/oj/account/login.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/static/src/js/app/oj/account/login.js b/static/src/js/app/oj/account/login.js index 5ef83b98..bc95cf5b 100644 --- a/static/src/js/app/oj/account/login.js +++ b/static/src/js/app/oj/account/login.js @@ -11,7 +11,14 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c method: "post", success: function (data) { if (!data.code) { - window.location.href = "/"; + //成功登陆 + var ref = document.referrer; + if(ref){ + if(ref.split("/")[2] == location.hostname){ + location.href = ref; + } + } + location.href = "/"; } else { bsAlert(data.data); From 97ffd604cf670c5c3a60d1ef12f9cd95eda36896 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 18 Aug 2015 16:03:49 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E7=BB=84?= =?UTF-8?q?=E5=86=85=E5=8F=AF=E8=A7=81=E7=9A=84=E5=85=AC=E5=91=8A=EF=BC=8C?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E4=BA=86=E5=AF=B9=E5=BA=94=E7=9A=84=20models?= =?UTF-8?q?=20=E5=92=8C=20js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0002_auto_20150818_1445.py | 26 +++ announcement/models.py | 4 + announcement/serializers.py | 4 + announcement/views.py | 52 +++++- oj/urls.py | 3 +- .../js/app/admin/announcement/announcement.js | 150 ++++++++++++++---- template/admin/announcement/announcement.html | 42 ++++- 7 files changed, 235 insertions(+), 46 deletions(-) create mode 100644 announcement/migrations/0002_auto_20150818_1445.py diff --git a/announcement/migrations/0002_auto_20150818_1445.py b/announcement/migrations/0002_auto_20150818_1445.py new file mode 100644 index 00000000..771b466a --- /dev/null +++ b/announcement/migrations/0002_auto_20150818_1445.py @@ -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, + ), + ] diff --git a/announcement/models.py b/announcement/models.py index 491c2192..1dfa6060 100644 --- a/announcement/models.py +++ b/announcement/models.py @@ -2,6 +2,7 @@ from django.db import models from account.models import User +from group.models import Group class Announcement(models.Model): @@ -17,6 +18,9 @@ class Announcement(models.Model): 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" diff --git a/announcement/serializers.py b/announcement/serializers.py index d6380b06..d896564a 100644 --- a/announcement/serializers.py +++ b/announcement/serializers.py @@ -8,6 +8,8 @@ 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(), allow_empty=True) class AnnouncementSerializer(serializers.ModelSerializer): @@ -28,3 +30,5 @@ class EditAnnouncementSerializer(serializers.Serializer): 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()) diff --git a/announcement/views.py b/announcement/views.py index 07d1e642..8059b908 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -5,6 +5,8 @@ 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) @@ -28,9 +30,26 @@ class AnnouncementAdminAPIView(APIView): serializer = CreateAnnouncementSerializer(data=request.data) if serializer.is_valid(): data = serializer.data - Announcement.objects.create(title=data["title"], - content=data["content"], - created_by=request.user) + 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) @@ -46,26 +65,43 @@ class AnnouncementAdminAPIView(APIView): if serializer.is_valid(): data = serializer.data try: - announcement = Announcement.objects.get(id=data["id"]) + if request.user.admin_type == SUPER_ADMIN: + announcement = Announcement.objects.get(id=data["id"]) + else: + announcement = Announcement.objects.get(id=data["id"], admin=request.user) except Announcement.DoesNotExist: - return error_response(u"该公告不存在!") + 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) - -class AnnouncementAPIView(APIView): def get(self, request): """ 公告分页json api接口 --- response_serializer: AnnouncementSerializer """ - announcement = Announcement.objects.all().order_by("-last_update_time") + if request.user.admin_type == SUPER_ADMIN: + announcement = Announcement.objects.all().order_by("-last_update_time") + else: + announcement = Announcement.objects.filter(admin=request.user) visible = request.GET.get("visible", None) if visible: announcement = announcement.filter(visible=(visible == "true")) diff --git a/oj/urls.py b/oj/urls.py index ba7c9977..ef9e5032 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -6,7 +6,7 @@ from django.views.generic import TemplateView from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView, EmailCheckAPIView, UserAdminAPIView, UserInfoAPIView) -from announcement.views import AnnouncementAPIView, AnnouncementAdminAPIView +from announcement.views import AnnouncementAdminAPIView from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView, JoinGroupAPIView, JoinGroupRequestAdminAPIView) @@ -38,7 +38,6 @@ urlpatterns = [ url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), url(r'^announcement/(?P\d+)/$', "announcement.views.announcement_page", name="announcement_page"), - url(r'^api/announcements/$', AnnouncementAPIView.as_view(), name="announcement_list_api"), url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), name="add_contest_page"), url(r'^problems/$', "problem.views.problem_list_page", name="problem_list_page"), diff --git a/static/src/js/app/admin/announcement/announcement.js b/static/src/js/app/admin/announcement/announcement.js index c4eebf65..7dd2051b 100644 --- a/static/src/js/app/admin/announcement/announcement.js +++ b/static/src/js/app/admin/announcement/announcement.js @@ -1,8 +1,5 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], function ($, avalon, csrfTokenHeader, bsAlert, editor) { - - - avalon.ready(function () { avalon.vmodels.announcement = null; @@ -20,7 +17,11 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], totalPage: 1, // 总页数 showVisibleOnly: false, //仅显示可见公告 // 编辑 + newTitle: "", announcementVisible: 0, + showGlobalViewRadio: true, + isGlobal: true, + allGroups: [], getState: function (el) { //获取公告当前状态,显示 if (el.visible) return "可见"; @@ -47,49 +48,75 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], }, editAnnouncement: function (announcement) { - $("#newTitle").val(announcement.title); + vm.newTitle = announcement.title; editAnnouncementEditor.setValue(announcement.content); vm.announcementVisible = announcement.visible; if (vm.editingAnnouncementId == announcement.id) vm.editingAnnouncementId = 0; else vm.editingAnnouncementId = announcement.id; + vm.isGlobal = announcement.is_global; + for (var i = 0; i < announcement.groups.length; i++) { + for (var j = 0; j < vm.allGroups.length; j++) { + if (announcement.groups[i] == vm.allGroups[j].id) { + vm.allGroups[j].isSelected = true; + } + } + } editAnnouncementEditor.focus(); }, cancelEdit: function () { vm.editingAnnouncementId = 0; }, submitChange: function () { - var title = $("#newTitle").val(); + var title = vm.newTitle; var content = editAnnouncementEditor.getValue(); - if (content && title) { - $.ajax({ - beforeSend: csrfTokenHeader, - url: "/api/admin/announcement/", - dataType: "json", - method: "put", - data: { - id: vm.editingAnnouncementId, - title: title, - content: content, - visible: vm.announcementVisible - }, - success: function (data) { - if (!data.code) { - bsAlert("修改成功"); - vm.editingAnnouncementId = 0; - getPageData(1); - } - else { - bsAlert(data.data); - } + if (content == "" || title == "") { + bsAlert("标题和内容都不能为空"); + return false; + } + + var selectedGroups = []; + if (!vm.isGlobal) { + for (var i = 0; i < vm.allGroups.length; i++) { + if (vm.allGroups[i].isSelected) { + selectedGroups.push(vm.allGroups[i].id); } - }); + } } - else { - bsAlert("标题和公告内容不得为空"); + + if (!vm.isGlobal && !selectedGroups.length) { + bsAlert("请至少选择一个小组"); + return false; } + + $.ajax({ + beforeSend: csrfTokenHeader, + url: "/api/admin/announcement/", + contentType: "application/json", + dataType: "json", + method: "put", + data: JSON.stringify({ + id: vm.editingAnnouncementId, + title: title, + content: content, + visible: vm.announcementVisible, + is_global: vm.isGlobal, + groups: selectedGroups + }), + success: function (data) { + if (!data.code) { + bsAlert("修改成功"); + vm.editingAnnouncementId = 0; + getPageData(1); + } + else { + bsAlert(data.data); + } + } + }); + } }); vm.$watch("showVisibleOnly", function () { @@ -98,8 +125,44 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], getPageData(1); + $.ajax({ + url: "/api/admin/group/", + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + if (!data.data.length) { + bsAlert("您的用户权限只能创建组内公告,但是您还没有创建过小组"); + return; + } + for (var i = 0; i < data.data.length; i++) { + var item = data.data[i]; + item["isSelected"] = false; + vm.allGroups.push(item); + } + } + else { + bsAlert(data.data); + } + } + }); + + $.ajax({ + url: "/api/user/", + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + if (data.data.admin_type == 1) { + vm.isGlobal = false; + vm.showGlobalViewRadio = false; + } + } + } + }); + function getPageData(page) { - var url = "/api/announcements/?paging=true&page=" + page + "&page_size=10"; + var url = "/api/admin/announcement/?paging=true&page=" + page + "&page_size=10"; if (vm.showVisibleOnly) url += "&visible=true"; $.ajax({ @@ -122,9 +185,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], } //新建公告表单验证与数据提交 - - - $('form').validator().on('submit', function (e) { + $("#announcement-form").validator().on('submit', function (e) { if (!e.isDefaultPrevented()) { var title = $("#title").val(); var content = createAnnouncementEditor.getValue(); @@ -132,10 +193,29 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], bsAlert("请填写公告内容"); return false; } + var selectedGroups = []; + if (!vm.isGlobal) { + for (var i = 0; i < vm.allGroups.length; i++) { + if (vm.allGroups[i].isSelected) { + selectedGroups.push(vm.allGroups[i].id); + } + } + } + + if (!vm.isGlobal && !selectedGroups.length) { + bsAlert("请至少选择一个小组"); + return false; + } $.ajax({ beforeSend: csrfTokenHeader, url: "/api/admin/announcement/", - data: {title: title, content: content}, + contentType: "application/json", + data: JSON.stringify({ + title: title, + content: content, + is_global: vm.isGlobal, + groups: selectedGroups + }), dataType: "json", method: "post", success: function (data) { @@ -148,7 +228,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "validator"], bsAlert(data.data); } } - }) + }); return false; } }) diff --git a/template/admin/announcement/announcement.html b/template/admin/announcement/announcement.html index 5c0d4d9a..3bfd605d 100644 --- a/template/admin/announcement/announcement.html +++ b/template/admin/announcement/announcement.html @@ -7,6 +7,7 @@ 创建时间 更新时间 创建者 + 可见范围 状态 @@ -16,6 +17,7 @@ {{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}} {{ el.last_update_time|date("yyyy-MM-dd HH:mm:ss")}} {{ el.created_by.username }} + {{ getState(el)}} @@ -36,7 +38,7 @@
-
+
@@ -44,6 +46,25 @@
+
+ + +
+ + 全局可见 + + + 小组内可见 + +
+ +
+
+ +
+   {{ el.name }} +
+
   @@ -65,6 +86,25 @@
+
+ + +
+ + 全局可见 + + + 小组内可见 + +
+ +
+
+ +
+   {{ el.name }} +
+