From 43be94cce63e1c396bdbce79eeb98dfc52889145 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 21 Sep 2015 11:06:11 +0800 Subject: [PATCH 01/14] =?UTF-8?q?=E6=AF=94=E8=B5=9B=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E4=B8=8D=E6=98=AF=E6=AD=A3=E5=9C=A8=E8=BF=9B=E8=A1=8C=E7=9A=84?= =?UTF-8?q?=E7=8A=B6=E6=80=81=EF=BC=8C=E5=B0=B1=E4=B8=8D=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E6=AF=94=E8=B5=9B=E7=9A=84=E5=88=86=E6=95=B0=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E5=92=8C=E5=A2=9E=E5=8A=A0=E6=AF=94=E8=B5=9B=E9=A2=98=E7=9B=AE?= =?UTF-8?q?=E8=AE=A1=E6=95=B0=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mq/scripts/mq.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mq/scripts/mq.py b/mq/scripts/mq.py index 567eebb1..b26a46fd 100644 --- a/mq/scripts/mq.py +++ b/mq/scripts/mq.py @@ -57,6 +57,9 @@ class MessageQueue(object): # 能运行到这里的都是比赛题目 try: contest = Contest.objects.get(id=submission.contest_id) + if contest.status != 0: + logger.info("Contest debug mode, id: " + str(contest.id) + ", submission id: " + submission_id) + continue contest_problem = ContestProblem.objects.get(contest=contest, id=submission.problem_id) except Contest.DoesNotExist: logger.warning("Submission contest does not exist, submission_id: " + submission_id) From 041fd2cb602c72299c1e96fb204be7b93d2fdc69 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 21 Sep 2015 11:16:30 +0800 Subject: [PATCH 02/14] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E9=83=A8=E5=88=86=20ma?= =?UTF-8?q?gic=20number?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/decorators.py | 10 +++++----- contest/models.py | 10 +++++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/contest/decorators.py b/contest/decorators.py index df73274a..600d98b8 100644 --- a/contest/decorators.py +++ b/contest/decorators.py @@ -3,12 +3,12 @@ from functools import wraps from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render -from django.utils.timezone import now from utils.shortcuts import error_response, error_page from account.models import SUPER_ADMIN -from .models import Contest +from .models import (Contest, PASSWORD_PROTECTED_CONTEST, PUBLIC_CONTEST, GROUP_CONTEST, + CONTEST_ENDED, CONTEST_NOT_START, CONTEST_UNDERWAY) def check_user_contest_permission(func): @@ -55,7 +55,7 @@ def check_user_contest_permission(func): return func(*args, **kwargs) # 有密码的公开赛 - if contest.contest_type == 2: + if contest.contest_type == PASSWORD_PROTECTED_CONTEST: # 没有输入过密码 if contest.id not in request.session.get("contests", []): if request.is_ajax(): @@ -65,7 +65,7 @@ def check_user_contest_permission(func): {"reason": "password_protect", "show_tab": False, "contest": contest}) # 指定小组参加的 - if contest.contest_type == 0: + if contest.contest_type == GROUP_CONTEST: if not contest.groups.filter(id__in=request.user.group_set.all()).exists(): if request.is_ajax(): return error_response(u"只有指定小组的可以参加这场比赛") @@ -74,7 +74,7 @@ def check_user_contest_permission(func): {"reason": "group_limited", "show_tab": False, "contest": contest}) # 比赛没有开始 - if contest.status == 1: + if contest.status == CONTEST_NOT_START: if request.is_ajax(): return error_response(u"比赛还没有开始") else: diff --git a/contest/models.py b/contest/models.py index 7331ec27..e0484555 100644 --- a/contest/models.py +++ b/contest/models.py @@ -10,6 +10,10 @@ GROUP_CONTEST = 0 PUBLIC_CONTEST = 1 PASSWORD_PROTECTED_CONTEST = 2 +CONTEST_NOT_START = 1 +CONTEST_ENDED = -1 +CONTEST_UNDERWAY = 0 + class Contest(models.Model): title = models.CharField(max_length=40, unique=True) @@ -44,13 +48,13 @@ class Contest(models.Model): def status(self): if self.start_time > now(): # 没有开始 返回1 - return 1 + return CONTEST_NOT_START elif self.end_time < now(): # 已经结束 返回0 - return -1 + return CONTEST_ENDED else: # 正在进行 返回0 - return 0 + return CONTEST_UNDERWAY class Meta: db_table = "contest" From 725d950e27f971cf2308049b938ec523b80abd5d Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 21 Sep 2015 12:28:22 +0800 Subject: [PATCH 03/14] =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=B8=8D=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E9=AA=8C=E8=AF=81=E7=A0=81=E7=9A=84=E6=97=B6=E5=80=99?= =?UTF-8?q?=E5=88=B7=E6=96=B0=E9=AA=8C=E8=AF=81=E4=BC=9A=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E9=94=99=E8=AF=AF=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/oj/account/login.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/static/src/js/app/oj/account/login.js b/static/src/js/app/oj/account/login.js index 32343c95..22d47593 100644 --- a/static/src/js/app/oj/account/login.js +++ b/static/src/js/app/oj/account/login.js @@ -33,7 +33,9 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c location.href = "/"; } else { - refresh_captcha(); + if(applied_captcha) { + refresh_captcha(); + } bsAlert(data.data); } } From cb0e7cf9382b94299b2df3b9c00d0e3d68aa98aa Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 21 Sep 2015 12:32:50 +0800 Subject: [PATCH 04/14] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=85=AC=E5=91=8A?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E4=B8=AD=E7=94=A8=E6=88=B7=E6=9D=83=E9=99=90?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E9=94=99=E8=AF=AF=E7=9A=84=E6=83=85=E5=86=B5?= =?UTF-8?q?=EF=BC=8C=E6=8A=BD=E5=8F=96=E4=BB=A3=E7=A0=81=E4=B8=BA=20decora?= =?UTF-8?q?tor=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- announcement/decorators.py | 43 +++++++++++++++++++++++++ announcement/models.py | 2 +- announcement/views.py | 22 +++---------- utils/templatetags/announcement_list.py | 1 + 4 files changed, 49 insertions(+), 19 deletions(-) create mode 100644 announcement/decorators.py diff --git a/announcement/decorators.py b/announcement/decorators.py new file mode 100644 index 00000000..96df987d --- /dev/null +++ b/announcement/decorators.py @@ -0,0 +1,43 @@ +# coding=utf-8 +from functools import wraps + +from django.http import HttpResponse, HttpResponseRedirect +from django.shortcuts import render + +from utils.shortcuts import error_response, error_page + +from account.models import SUPER_ADMIN +from .models import Announcement + + +def check_user_announcement_permission(func): + @wraps(func) + def _check_user_announcement_permission(*args, **kwargs): + """ + 这个函数检测当前用户能否查看这个公告 + """ + # CBV 的情况,第一个参数是self,第二个参数是request + if len(args) == 2: + request = args[-1] + else: + request = args[0] + + if "announcement_id" not in kwargs: + return error_page(request, u"参数错误") + announcement_id = kwargs["announcement_id"] + + try: + announcement = Announcement.objects.get(id=announcement_id, visible=True) + except Announcement.DoesNotExist: + return error_page(request, u"公告不存在") + + # 如果公告是只有部分小组可见的 + if not announcement.is_global: + # 用户必须是登录状态的 + if not request.user.is_authenticated(): + return HttpResponseRedirect("/login/") + if not announcement.groups.filter(id__in=request.user.group_set.all()).exists(): + return error_page(request, u"公告不存在") + return func(*args, **kwargs) + + return _check_user_announcement_permission diff --git a/announcement/models.py b/announcement/models.py index 1dfa6060..c3267f12 100644 --- a/announcement/models.py +++ b/announcement/models.py @@ -18,7 +18,7 @@ class Announcement(models.Model): last_update_time = models.DateTimeField(auto_now=True) # 是否可见 false的话相当于删除 visible = models.BooleanField(default=True) - # 公告可见范围 0是全局可见 1是部分小组可见,需要在下面的字段中存储可见的小组 + # 公告可见范围 True 是全局可见 False 是部分小组可见,需要在下面的字段中存储可见的小组 is_global = models.BooleanField() groups = models.ManyToManyField(Group) diff --git a/announcement/views.py b/announcement/views.py index 6790e8ce..3160d9ef 100644 --- a/announcement/views.py +++ b/announcement/views.py @@ -10,27 +10,13 @@ from group.models import Group from .models import Announcement from .serializers import (CreateAnnouncementSerializer, AnnouncementSerializer, EditAnnouncementSerializer) +from .decorators import check_user_announcement_permission +@check_user_announcement_permission def announcement_page(request, announcement_id): - try: - announcement = Announcement.objects.get(id=announcement_id, visible=True) - except Announcement.DoesNotExist: - return error_page(request, u"公告不存在") - # 公开的公告 - if announcement.is_global == 0: - return render(request, "oj/announcement/announcement.html", {"announcement": announcement}) - else: - if not request.user.is_authenticated(): - return error_page(request, u"公告不存在") - # 判断是不是在组里面 - if request.user.admin_type == SUPER_ADMIN or request.user == announcement.created_by: - return render(request, "oj/announcement/announcement.html", {"announcement": announcement}) - else: - if request.user.groups.filter(id__in=[item.id for item in announcement.groups.all()]).exists(): - return render(request, "oj/announcement/announcement.html", {"announcement": announcement}) - else: - return error_page(request, u"公告不存在") + announcement = Announcement.objects.get(id=announcement_id, visible=True) + return render(request, "oj/announcement/announcement.html", {"announcement": announcement}) class AnnouncementAdminAPIView(APIView): diff --git a/utils/templatetags/announcement_list.py b/utils/templatetags/announcement_list.py index acf38d9f..1b2f331e 100644 --- a/utils/templatetags/announcement_list.py +++ b/utils/templatetags/announcement_list.py @@ -3,6 +3,7 @@ from django import template from announcement.models import Announcement + def public_announcement_list(): return Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time") From 60d727d7b23a07a71d6b8593fb773d22254cf0c5 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 21 Sep 2015 13:41:14 +0800 Subject: [PATCH 05/14] =?UTF-8?q?=E5=88=A0=E9=99=A4=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/admin/admin.js | 3 +-- template/src/admin/admin.html | 39 ----------------------------- template/src/admin/index/index.html | 2 +- 3 files changed, 2 insertions(+), 42 deletions(-) diff --git a/static/src/js/app/admin/admin.js b/static/src/js/app/admin/admin.js index 878b9cad..d68dba11 100644 --- a/static/src/js/app/admin/admin.js +++ b/static/src/js/app/admin/admin.js @@ -24,8 +24,7 @@ require(["jquery", "avalon", "bootstrap"], function ($, avalon) { var superAdminNav = [ { name: "首页", children: [{name: "主页", hash: "#index/index"}, - {name: "监控", hash: "#monitor/monitor"}, - {name: "统计", hash: "#statistics/statistics"}] + {name: "监控", hash: "#monitor/monitor"}] }, { name: "通用", diff --git a/template/src/admin/admin.html b/template/src/admin/admin.html index 0e470c6e..7020dae3 100644 --- a/template/src/admin/admin.html +++ b/template/src/admin/admin.html @@ -71,45 +71,6 @@ {{ item.name }} - diff --git a/template/src/admin/index/index.html b/template/src/admin/index/index.html index 61168280..257eab15 100644 --- a/template/src/admin/index/index.html +++ b/template/src/admin/index/index.html @@ -1 +1 @@ -

Hello world

\ No newline at end of file +

Online Judge Admin

\ No newline at end of file From 5f23bff3c5a9040c4292fdd0a3c231e02eb07a71 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 21 Sep 2015 13:56:54 +0800 Subject: [PATCH 06/14] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=89=8D=E5=8F=B0?= =?UTF-8?q?=E9=A2=98=E7=9B=AE=E7=9A=84=E9=87=8D=E6=96=B0=E5=88=A4=E9=A2=98?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 4 ++- .../js/app/admin/problem/submissionList.js | 14 +++++++++ submission/serializers.py | 4 +++ submission/views.py | 30 ++++++++++++++++++- .../src/admin/problem/submission_list.html | 1 + 5 files changed, 51 insertions(+), 2 deletions(-) diff --git a/oj/urls.py b/oj/urls.py index 6f16acf8..5b65e4c0 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -17,7 +17,8 @@ from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView, from admin.views import AdminTemplateView from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView -from submission.views import SubmissionAPIView, SubmissionAdminAPIView, SubmissionShareAPIView +from submission.views import (SubmissionAPIView, SubmissionAdminAPIView, + SubmissionShareAPIView, SubmissionRejudgeAdminAPIView) from contest_submission.views import ContestSubmissionAPIView, ContestSubmissionAdminAPIView from monitor.views import QueueLengthMonitorAPIView from utils.views import SimditorImageUploadAPIView @@ -119,4 +120,5 @@ urlpatterns = [ url(r'^api/account_security_check/$', AccountSecurityAPIView.as_view(), name="account_security_check"), url(r'^api/contest/time/$', ContestTimeAPIView.as_view(), name="contest_time_api_view"), + url(r'^api/admin/rejudge/$', SubmissionRejudgeAdminAPIView.as_view(), name="submission_rejudge_api"), ] diff --git a/static/src/js/app/admin/problem/submissionList.js b/static/src/js/app/admin/problem/submissionList.js index 1af2e6ce..e8d00bb0 100644 --- a/static/src/js/app/admin/problem/submissionList.js +++ b/static/src/js/app/admin/problem/submissionList.js @@ -51,6 +51,20 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT }, showProblemListPage: function(){ vm.$fire("up!showProblemListPage"); + }, + rejudge: function(submission_id){ + $.ajax({ + beforeSend: csrfTokenHeader, + url: "/api/admin/rejudge/", + method: "post", + data: {"submission_id": submission_id}, + success: function(data){ + if(!data.code){ + bsAlert("重判任务提交成功"); + } + } + + }) } }); } diff --git a/submission/serializers.py b/submission/serializers.py index 8fd62787..3a5308cd 100644 --- a/submission/serializers.py +++ b/submission/serializers.py @@ -26,3 +26,7 @@ class SubmissionhareSerializer(serializers.Serializer): submission_id = serializers.CharField(max_length=40) +class SubmissionRejudgeSerializer(serializers.Serializer): + submission_id = serializers.CharField(max_length=40) + + diff --git a/submission/views.py b/submission/views.py index 6eb69fae..396cdfe8 100644 --- a/submission/views.py +++ b/submission/views.py @@ -19,7 +19,8 @@ from announcement.models import Announcement from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate from .models import Submission -from .serializers import CreateSubmissionSerializer, SubmissionSerializer, SubmissionhareSerializer +from .serializers import (CreateSubmissionSerializer, SubmissionSerializer, + SubmissionhareSerializer, SubmissionRejudgeSerializer) logger = logging.getLogger("app_info") @@ -219,3 +220,30 @@ class SubmissionShareAPIView(APIView): return success_response(submission.shared) else: return serializer_invalid_response(serializer) + + +class SubmissionRejudgeAdminAPIView(APIView): + def post(self, request): + serializer = SubmissionRejudgeSerializer(data=request.data) + if serializer.is_valid(): + submission_id = serializer.data["submission_id"] + try: + submission = Submission.objects.get(id=submission_id) + except Submission.DoesNotExist: + return error_response(u"提交不存在") + # 目前只考虑前台公开题目的重新判题 + try: + problem = Problem.objects.get(id=submission.problem_id) + except Problem.DoesNotExist: + return error_response(u"题目不存在") + try: + judge.delay(submission_id, problem.time_limit, problem.memory_limit, problem.test_case_id) + except Exception as e: + logger.error(e) + return error_response(u"提交判题任务失败") + + # 增加redis 中判题队列长度的计数器 + r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"]) + r.incr("judge_queue_length") + else: + return serializer_invalid_response(serializer) \ No newline at end of file diff --git a/template/src/admin/problem/submission_list.html b/template/src/admin/problem/submission_list.html index 28eaebd4..e0880af5 100644 --- a/template/src/admin/problem/submission_list.html +++ b/template/src/admin/problem/submission_list.html @@ -22,6 +22,7 @@ {{ results[el.result] }} 详情 + 重判 From 30df3eadb81339c6046c21851768bb47f0edb16a Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Mon, 21 Sep 2015 23:10:26 +0800 Subject: [PATCH 07/14] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20https=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=89=8D=E7=AB=AF=E8=8E=B7=E5=8F=96=E5=88=B0=E7=9A=84?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E6=98=AF=20http=20=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/server_settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/oj/server_settings.py b/oj/server_settings.py index 77ca1fd0..9f8f084e 100644 --- a/oj/server_settings.py +++ b/oj/server_settings.py @@ -41,3 +41,5 @@ STATICFILES_DIRS = [os.path.join(BASE_DIR, "static/release/"), os.path.join(BASE # 模板文件夹 TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/release/')] + +SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') From 0718b4c521213248a07456205ac4996237425703 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 22 Sep 2015 13:09:01 +0800 Subject: [PATCH 08/14] fix typo --- submission/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/submission/views.py b/submission/views.py index 396cdfe8..70ee700e 100644 --- a/submission/views.py +++ b/submission/views.py @@ -245,5 +245,6 @@ class SubmissionRejudgeAdminAPIView(APIView): # 增加redis 中判题队列长度的计数器 r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"]) r.incr("judge_queue_length") + return success_response(u"任务提交成功") else: return serializer_invalid_response(serializer) \ No newline at end of file From 5ac16a3c7f5dbad73e2851a0123be7c23a1f8f92 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 22 Sep 2015 14:28:46 +0800 Subject: [PATCH 09/14] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E6=89=80=E6=9C=89=E6=8F=90=E4=BA=A4=E7=9A=84=E8=B0=83=E8=AF=95?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- submission/views.py | 30 ++++++++++++------- .../src/oj/problem/my_submissions_list.html | 5 +++- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/submission/views.py b/submission/views.py index 70ee700e..88c6d0dc 100644 --- a/submission/views.py +++ b/submission/views.py @@ -11,7 +11,7 @@ from rest_framework.views import APIView from judge.judger_controller.tasks import judge from judge.judger_controller.settings import redis_config from account.decorators import login_required -from account.models import SUPER_ADMIN +from account.models import SUPER_ADMIN, User from problem.models import Problem from contest.models import ContestProblem, Contest @@ -162,7 +162,11 @@ def my_submission_list_page(request, page=1): 我的所有提交的列表页 """ submissions = Submission.objects.filter(user_id=request.user.id, contest_id__isnull=True). \ - values("id", "problem_id", "result", "create_time", "accepted_answer_time", "language").order_by("-create_time") + values("id", "user_id", "problem_id", "result", "create_time", "accepted_answer_time", "language").order_by("-create_time") + + # 显示所有人的提交 这是管理员的调试功能 + show_all = request.GET.get("show_all", False) == "true" + language = request.GET.get("language", None) filter = None if language: @@ -173,14 +177,21 @@ def my_submission_list_page(request, page=1): submissions = submissions.filter(result=int(result)) filter = {"name": "result", "content": result} - # 为 submission 查询题目 因为提交页面经常会有重复的题目,缓存一下查询结果 - cache_result = {} + # 因为提交页面经常会有重复的题目和用户,缓存一下查询结果 + cache_result = {"problem": {}, "user": {}} for item in submissions: problem_id = item["problem_id"] - if problem_id not in cache_result: + if problem_id not in cache_result["problem"]: problem = Problem.objects.get(id=problem_id) - cache_result[problem_id] = problem.title - item["title"] = cache_result[problem_id] + cache_result["problem"][problem_id] = problem.title + item["title"] = cache_result["problem"][problem_id] + + if show_all: + user_id = item["user_id"] + if user_id not in cache_result["user"]: + user = User.objects.get(id=user_id) + cache_result["user"][user_id] = user + item["user"] = cache_result["user"][user_id] paginator = Paginator(submissions, 20) try: @@ -197,13 +208,10 @@ def my_submission_list_page(request, page=1): except Exception: pass - # 右侧的公告列表 - announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time") - return render(request, "oj/submission/my_submissions_list.html", {"submissions": current_page, "page": int(page), "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20, - "announcements": announcements, "filter": filter}) + "filter": filter, "show_all": show_all}) class SubmissionShareAPIView(APIView): diff --git a/template/src/oj/problem/my_submissions_list.html b/template/src/oj/problem/my_submissions_list.html index dd915a17..4f69d6fd 100644 --- a/template/src/oj/problem/my_submissions_list.html +++ b/template/src/oj/problem/my_submissions_list.html @@ -27,7 +27,10 @@ {% for item in submissions %} - {{ forloop.counter }} + + {{ forloop.counter }} + {% if show_all %}{{ item.user.username }}{% endif %} + {{ item.create_time }} {{ item.language|translate_language }} From f0a70c3b0942ebaae6805686c69e3f36aaf86008 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 22 Sep 2015 14:37:45 +0800 Subject: [PATCH 10/14] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- submission/views.py | 8 +++++--- template/src/oj/problem/my_submissions_list.html | 1 - template/src/oj/submission/my_submissions_list.html | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/submission/views.py b/submission/views.py index 88c6d0dc..c0487e51 100644 --- a/submission/views.py +++ b/submission/views.py @@ -161,11 +161,13 @@ def my_submission_list_page(request, page=1): """ 我的所有提交的列表页 """ - submissions = Submission.objects.filter(user_id=request.user.id, contest_id__isnull=True). \ - values("id", "user_id", "problem_id", "result", "create_time", "accepted_answer_time", "language").order_by("-create_time") - # 显示所有人的提交 这是管理员的调试功能 show_all = request.GET.get("show_all", False) == "true" + if show_all: + submissions = Submission.objects.filter(contest_id__isnull=True) + else: + submissions = Submission.objects.filter(user_id=request.user.id, contest_id__isnull=True) + submissions = submissions.values("id", "user_id", "problem_id", "result", "create_time", "accepted_answer_time", "language").order_by("-create_time") language = request.GET.get("language", None) filter = None diff --git a/template/src/oj/problem/my_submissions_list.html b/template/src/oj/problem/my_submissions_list.html index 4f69d6fd..faa9d628 100644 --- a/template/src/oj/problem/my_submissions_list.html +++ b/template/src/oj/problem/my_submissions_list.html @@ -29,7 +29,6 @@ {{ forloop.counter }} - {% if show_all %}{{ item.user.username }}{% endif %} {{ item.create_time }} diff --git a/template/src/oj/submission/my_submissions_list.html b/template/src/oj/submission/my_submissions_list.html index 1e9975fd..251c5e8c 100644 --- a/template/src/oj/submission/my_submissions_list.html +++ b/template/src/oj/submission/my_submissions_list.html @@ -52,6 +52,7 @@ {{ forloop.counter |add:start_id }} + {% if show_all %}({{ item.user.username }}){% endif %} {{ item.title }} From a57544db1d096a566fa3da09cf29fdca523b50b7 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 22 Sep 2015 16:17:53 +0800 Subject: [PATCH 11/14] =?UTF-8?q?=E5=8F=AA=E6=9C=89=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=91=98=E6=89=8D=E8=83=BD=E6=9F=A5=E7=9C=8B=E6=89=80=E6=9C=89?= =?UTF-8?q?=E4=BA=BA=E7=9A=84=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- submission/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submission/views.py b/submission/views.py index c0487e51..05dbd1ed 100644 --- a/submission/views.py +++ b/submission/views.py @@ -162,7 +162,7 @@ def my_submission_list_page(request, page=1): 我的所有提交的列表页 """ # 显示所有人的提交 这是管理员的调试功能 - show_all = request.GET.get("show_all", False) == "true" + show_all = request.GET.get("show_all", False) == "true" and request.user.admin_type == SUPER_ADMIN if show_all: submissions = Submission.objects.filter(contest_id__isnull=True) else: From 172d45eb78092f8a2f229af7e2a7eb3c4201042d Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 22 Sep 2015 16:18:32 +0800 Subject: [PATCH 12/14] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=91=98=E6=9D=83=E9=99=90=E7=9A=84=20decora?= =?UTF-8?q?tor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/decorators.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/account/decorators.py b/account/decorators.py index 4113dd01..1181c6a5 100644 --- a/account/decorators.py +++ b/account/decorators.py @@ -4,7 +4,7 @@ from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render from utils.shortcuts import error_response, error_page -from .models import User +from .models import User, SUPER_ADMIN def login_required(func): @@ -12,7 +12,10 @@ def login_required(func): def check(*args, **kwargs): # 在class based views 里面,args 有两个元素,一个是self, 第二个才是request, # 在function based views 里面,args 只有request 一个参数 - request = args[-1] + if len(args) == 2: + request = args[-1] + else: + request = args[0] if request.user.is_authenticated(): return func(*args, **kwargs) if request.is_ajax(): @@ -25,7 +28,10 @@ def login_required(func): def admin_required(func): @wraps(func) def check(*args, **kwargs): - request = args[-1] + if len(args) == 2: + request = args[-1] + else: + request = args[0] if request.user.is_authenticated() and request.user.admin_type: return func(*args, **kwargs) if request.is_ajax(): @@ -33,3 +39,20 @@ def admin_required(func): else: return error_page(request, u"需要管理员权限,如果没有登录,请先登录") return check + + +def super_admin_required(func): + @wraps(func) + def check(*args, **kwargs): + if len(args) == 2: + request = args[-1] + else: + request = args[0] + if request.user.is_authenticated() and request.user.admin_type == SUPER_ADMIN: + return func(*args, **kwargs) + if request.is_ajax(): + return error_response(u"需要超级管理员权限") + else: + return error_page(request, u"需要超级管理员权限") + + return check \ No newline at end of file From 707e38ac79e0260a60c7a8d1b23a2282fc924797 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 22 Sep 2015 16:19:08 +0800 Subject: [PATCH 13/14] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=EF=BC=8C=E4=BF=AE=E8=A1=A5=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E5=88=A4=E6=96=AD=E6=9D=83=E9=99=90=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problem/decorators.py | 28 ++++++++++++++++++++++++++ problem/views.py | 46 ++++++++++++++++++++++++++++++++----------- 2 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 problem/decorators.py diff --git a/problem/decorators.py b/problem/decorators.py new file mode 100644 index 00000000..762c54a7 --- /dev/null +++ b/problem/decorators.py @@ -0,0 +1,28 @@ +# coding=utf-8 +from functools import wraps + +from account.models import SUPER_ADMIN +from utils.shortcuts import error_response +from .models import Problem + + +def check_user_problem_permission(func): + @wraps(func) + def check(*args, **kwargs): + # 在class based views 里面,args 有两个元素,一个是self, 第二个才是request, + # 在function based views 里面,args 只有request 一个参数 + if len(args) == 2: + request = args[-1] + else: + request = args[0] + + # 这是在后台使用的url middleware 已经确保用户是登录状态的了 + if request.user.admin_type == SUPER_ADMIN: + return func(*args, **kwargs) + try: + Problem.objects.get(id=request.data.get("problem_id", -1), created_by=request.user) + except Problem.DoesNotExist: + return error_response(u"问题不存在") + return func(*args, **kwargs) + + return check diff --git a/problem/views.py b/problem/views.py index dbe125ad..8dc42d63 100644 --- a/problem/views.py +++ b/problem/views.py @@ -4,6 +4,7 @@ import re import os import hashlib import json +import logging from django.shortcuts import render from django.db.models import Q, Count @@ -13,19 +14,23 @@ from rest_framework.views import APIView from django.conf import settings - -from announcement.models import Announcement +from account.models import SUPER_ADMIN +from account.decorators import super_admin_required from utils.shortcuts import (serializer_invalid_response, error_response, success_response, paginate, rand_str, error_page) from .serizalizers import (CreateProblemSerializer, EditProblemSerializer, ProblemSerializer, ProblemTagSerializer, CreateProblemTagSerializer) from .models import Problem, ProblemTag -import logging +from .decorators import check_user_problem_permission + logger = logging.getLogger("app_info") def problem_page(request, problem_id): + """ + 前台题目详情页 + """ try: problem = Problem.objects.get(id=problem_id, visible=True) except Problem.DoesNotExist: @@ -34,11 +39,15 @@ def problem_page(request, problem_id): class ProblemTagAdminAPIView(APIView): + """ + 获取所有标签的列表 + """ def get(self, request): return success_response(ProblemTagSerializer(ProblemTag.objects.all(), many=True).data) class ProblemAdminAPIView(APIView): + @super_admin_required def post(self, request): """ 题目发布json api接口 @@ -72,6 +81,7 @@ class ProblemAdminAPIView(APIView): else: return serializer_invalid_response(serializer) + @check_user_problem_permission def put(self, request): """ 题目编辑json api接口 @@ -82,11 +92,7 @@ class ProblemAdminAPIView(APIView): serializer = EditProblemSerializer(data=request.data) if serializer.is_valid(): data = serializer.data - try: - problem = Problem.objects.get(id=data["id"]) - except Problem.DoesNotExist: - return error_response(u"该题目不存在!") - + problem = Problem.objects.get(id=data["id"]) problem.title = data["title"] problem.description = data["description"] problem.input_description = data["input_description"] @@ -123,23 +129,36 @@ class ProblemAdminAPIView(APIView): problem_id = request.GET.get("problem_id", None) if problem_id: try: + # 普通管理员只能获取自己创建的题目 + # 超级管理员可以获取全部的题目 problem = Problem.objects.get(id=problem_id) + if request.user.admin_type != SUPER_ADMIN: + problem = problem.get(created_by=request.user) return success_response(ProblemSerializer(problem).data) except Problem.DoesNotExist: return error_response(u"题目不存在") - problem = Problem.objects.all().order_by("-create_time") + + # 获取问题列表 + problems = Problem.objects.all().order_by("-create_time") + + if request.user.admin_type != SUPER_ADMIN: + problems = problems.filter(created_by=request.user) + visible = request.GET.get("visible", None) if visible: - problem = problem.filter(visible=(visible == "true")) + problems = problems.filter(visible=(visible == "true")) keyword = request.GET.get("keyword", None) if keyword: - problem = problem.filter(Q(title__contains=keyword) | + problems = problems.filter(Q(title__contains=keyword) | Q(description__contains=keyword)) - return paginate(request, problem, ProblemSerializer) + return paginate(request, problems, ProblemSerializer) class TestCaseUploadAPIView(APIView): + """ + 上传题目的测试用例 + """ def _is_legal_test_case_file_name(self, file_name): # 正整数开头的 .in 或者.out 结尾的 regex = r"^[1-9]\d*\.(in|out)$" @@ -237,6 +256,9 @@ class TestCaseUploadAPIView(APIView): def problem_list_page(request, page=1): + """ + 前台的问题列表 + """ # 正常情况 problems = Problem.objects.filter(visible=True) From 389c1905a6f4dd1a75c3e4d6ed901ad332cb1188 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Tue, 22 Sep 2015 16:19:40 +0800 Subject: [PATCH 14/14] =?UTF-8?q?=E4=BF=AE=E8=A1=A5=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E5=88=A4=E6=96=AD=E6=AF=94=E8=B5=9B=E6=9D=83?= =?UTF-8?q?=E9=99=90=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=9B=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=AF=94=E8=B5=9B=E8=B0=83=E8=AF=95=E6=A8=A1=E5=BC=8F=E7=9A=84?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/views.py | 60 ++++++++++++-------- template/src/oj/contest/contest_problem.html | 4 +- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/contest/views.py b/contest/views.py index 1869bb5f..8e0a2ac4 100644 --- a/contest/views.py +++ b/contest/views.py @@ -16,7 +16,7 @@ from utils.shortcuts import (serializer_invalid_response, error_response, from account.models import SUPER_ADMIN, User from account.decorators import login_required from group.models import Group -from .models import Contest, ContestProblem, ContestSubmission +from .models import Contest, ContestProblem, ContestSubmission, CONTEST_ENDED, CONTEST_NOT_START, CONTEST_UNDERWAY from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST from .decorators import check_user_contest_permission from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer, @@ -45,10 +45,10 @@ class ContestAdminAPIView(APIView): if data["contest_type"] in [PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST]: if request.user.admin_type != SUPER_ADMIN: return error_response(u"只有超级管理员才可创建公开赛") + if data["contest_type"] == PASSWORD_PROTECTED_CONTEST: if not data["password"]: return error_response(u"此比赛为有密码的公开赛,密码不可为空") - # 没有密码的公开赛 没有密码的小组赛 elif data["contest_type"] == GROUP_CONTEST: if request.user.admin_type == SUPER_ADMIN: @@ -58,7 +58,7 @@ class ContestAdminAPIView(APIView): if not groups.count(): return error_response(u"请至少选择一个小组") if data["start_time"] >= data["end_time"]: - return error_response(u"比赛的开始时间不能晚于或等于比赛结束的时间") + return error_response(u"比赛的开始时间必须早于比赛结束的时间") try: contest = Contest.objects.create(title=data["title"], description=data["description"], mode=data["mode"], contest_type=data["contest_type"], @@ -86,7 +86,10 @@ class ContestAdminAPIView(APIView): data = serializer.data groups = [] try: + # 超级管理员可以编辑所有的 contest = Contest.objects.get(id=data["id"]) + if request.user.admin_type != SUPER_ADMIN: + contest = contest.get(created_by=request.user) except Contest.DoesNotExist: return error_response(u"该比赛不存在!") try: @@ -109,9 +112,8 @@ class ContestAdminAPIView(APIView): if not groups.count(): return error_response(u"请至少选择一个小组") if data["start_time"] >= data["end_time"]: - return error_response(u"比赛的开始时间不能晚于或等于比赛结束的时间") - if request.user.admin_type != SUPER_ADMIN and request.user != contest.created_by: - return error_response(u"你无权修改该比赛!") + return error_response(u"比赛的开始时间必须早于比赛结束的时间") + contest.title = data["title"] contest.description = data["description"] contest.mode = data["mode"] @@ -163,6 +165,8 @@ class ContestProblemAdminAPIView(APIView): data = serializer.data try: contest = Contest.objects.get(id=data["contest_id"]) + if request.user.admin_type != SUPER_ADMIN: + contest = contest.get(created_by=request.user) except Contest.DoesNotExist: return error_response(u"比赛不存在") contest_problem = ContestProblem.objects.create(title=data["title"], @@ -199,7 +203,7 @@ class ContestProblemAdminAPIView(APIView): return error_response(u"该比赛题目不存在!") contest = Contest.objects.get(id=contest_problem.contest_id) if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user: - return error_response(u"你无权修改该题目!") + return error_response(u"比赛不存在") contest_problem.title = data["title"] contest_problem.description = data["description"] contest_problem.input_description = data["input_description"] @@ -227,29 +231,27 @@ class ContestProblemAdminAPIView(APIView): if contest_problem_id: try: contest_problem = ContestProblem.objects.get(id=contest_problem_id) + if request.user.admin_type != SUPER_ADMIN: + contest_problem = contest_problem.get(created_by=request.user) return success_response(ContestProblemSerializer(contest_problem).data) except ContestProblem.DoesNotExist: return error_response(u"比赛题目不存在") - if request.user.admin_type == SUPER_ADMIN: - contest_problem = ContestProblem.objects.all().order_by("sort_index") - else: - contest_problem = ContestProblem.objects.filter(created_by=request.user).order_by("sort_index") + + contest_problems = ContestProblem.objects.all().order_by("sort_index") + if request.user.admin_type != SUPER_ADMIN: + contest_problems = contest_problems.filter(created_by=request.user).order_by("sort_index") visible = request.GET.get("visible", None) if visible: - contest_problem = contest_problem.filter(visible=(visible == "true")) + contest_problems = contest_problems.filter(visible=(visible == "true")) keyword = request.GET.get("keyword", None) if keyword: - contest_problem = contest_problem.filter(Q(title__contains=keyword) | + contest_problems = contest_problems.filter(Q(title__contains=keyword) | Q(description__contains=keyword)) contest_id = request.GET.get("contest_id", None) if contest_id: - try: - contest = Contest.objects.get(id=contest_id) - except Contest.DoesNotExist: - return error_response(u"该比赛不存在!") - contest_problem = contest_problem.filter(contest=contest).order_by("sort_index") + contest_problem = contest_problems.filter(contest__id=contest_id).order_by("sort_index") - return paginate(request, contest_problem, ContestProblemSerializer) + return paginate(request, contest_problems, ContestProblemSerializer) class ContestPasswordVerifyAPIView(APIView): @@ -306,12 +308,23 @@ def contest_problem_page(request, contest_id, contest_problem_id): pass # 已经结束 - if contest.status == -1: + if contest.status == CONTEST_ENDED: show_warning = True warning = u"比赛已经结束" + elif contest.status == CONTEST_NOT_START: + show_warning = True + warning = u"比赛没有开始,您是管理员,可以提交和测试题目,但是目前的提交不会计入排名。" + + show_submit_code_area = False + if contest.status == CONTEST_UNDERWAY: + show_submit_code_area = True + if request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by: + show_submit_code_area = True + return render(request, "oj/contest/contest_problem.html", {"contest_problem": contest_problem, "contest": contest, "samples": json.loads(contest_problem.samples), - "show_warning": show_warning, "warning": warning}) + "show_warning": show_warning, "warning": warning, + "show_submit_code_area": show_submit_code_area}) @check_user_contest_permission @@ -319,10 +332,7 @@ def contest_problems_list_page(request, contest_id): """ 比赛所有题目的列表页 """ - try: - contest = Contest.objects.get(id=contest_id) - except Contest.DoesNotExist: - return error_page(request, u"比赛不存在") + contest = Contest.objects.get(id=contest_id) contest_problems = ContestProblem.objects.filter(contest=contest).order_by("sort_index") submissions = ContestSubmission.objects.filter(user=request.user, contest=contest) state = {} diff --git a/template/src/oj/contest/contest_problem.html b/template/src/oj/contest/contest_problem.html index 64cff29a..0b07c566 100644 --- a/template/src/oj/contest/contest_problem.html +++ b/template/src/oj/contest/contest_problem.html @@ -60,7 +60,7 @@
{{ contest_problem.hint|safe }}
{% endif %} - {% ifequal contest.status 0 %} + {% if show_submit_code_area %}
@@ -86,7 +86,7 @@
- {% endifequal %} + {% endif %} {% if show_warning %} {% endif %}