From fdf6b39c783601d249fc4097ac3e86a031a9ae6c Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 22 Aug 2015 12:31:28 +0800 Subject: [PATCH 1/7] add message to mq --- judge/judger_controller/tasks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/judge/judger_controller/tasks.py b/judge/judger_controller/tasks.py index ceb3f4e..8382deb 100644 --- a/judge/judger_controller/tasks.py +++ b/judge/judger_controller/tasks.py @@ -38,4 +38,5 @@ def judge(submission_id, time_limit, memory_limit, test_case_id): conn.commit() conn.close() r = redis.Redis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"]) - r.decr("judge_queue_length") \ No newline at end of file + r.decr("judge_queue_length") + r.lpush("queue", submission_id) From 46d2ea35c2e6884eac21232078d29c14ae0f1647 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 22 Aug 2015 12:55:44 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=A2=98=E7=9B=AE?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=BB=93=E6=9E=9C=E8=AE=A1=E6=95=B0=E5=99=A8?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E6=B8=85=E9=9B=B6=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/oj/problem/problem.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/static/src/js/app/oj/problem/problem.js b/static/src/js/app/oj/problem/problem.js index cb3127a..009929b 100644 --- a/static/src/js/app/oj/problem/problem.js +++ b/static/src/js/app/oj/problem/problem.js @@ -59,6 +59,7 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro if(counter++ > 10){ hideLoading(); bsAlert("抱歉,服务器可能出现了故障,请稍后到我的提交列表中查看"); + counter = 0; return; } $.ajax({ @@ -73,6 +74,7 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro setTimeout(getResult, 1000); } else { + counter = 0; hideLoading(); $("#result").html(getResultHtml(data.data)); } From c12c227ee9bb32e15aa9e7ee673d7c2ce518875a Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 22 Aug 2015 12:56:22 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20redis=20=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E9=98=9F=E5=88=97=E6=9D=A5=E4=BC=A0=E9=80=92=E9=A2=98?= =?UTF-8?q?=E7=9B=AE=E7=9A=84=E7=BB=93=E6=9E=9C=E3=80=82=E4=BB=8E=E8=80=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=A2=98=E7=9B=AE=20ac=20=E5=92=8C=20ts=20?= =?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 --- judge/judger_controller/tasks.py | 2 +- mq/__init__.py | 1 + mq/models.py | 1 + mq/scripts/__init__.py | 1 + mq/scripts/info.py | 37 ++++++++++++++++++++++++++++++++ oj/settings.py | 3 +++ submission/views.py | 12 ----------- 7 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 mq/__init__.py create mode 100644 mq/models.py create mode 100644 mq/scripts/__init__.py create mode 100644 mq/scripts/info.py diff --git a/judge/judger_controller/tasks.py b/judge/judger_controller/tasks.py index 8382deb..cc3d7d5 100644 --- a/judge/judger_controller/tasks.py +++ b/judge/judger_controller/tasks.py @@ -1,5 +1,5 @@ # coding=utf-8 -import datetime +import json import redis import MySQLdb import subprocess diff --git a/mq/__init__.py b/mq/__init__.py new file mode 100644 index 0000000..9bad579 --- /dev/null +++ b/mq/__init__.py @@ -0,0 +1 @@ +# coding=utf-8 diff --git a/mq/models.py b/mq/models.py new file mode 100644 index 0000000..9bad579 --- /dev/null +++ b/mq/models.py @@ -0,0 +1 @@ +# coding=utf-8 diff --git a/mq/scripts/__init__.py b/mq/scripts/__init__.py new file mode 100644 index 0000000..9bad579 --- /dev/null +++ b/mq/scripts/__init__.py @@ -0,0 +1 @@ +# coding=utf-8 diff --git a/mq/scripts/info.py b/mq/scripts/info.py new file mode 100644 index 0000000..a4fa50e --- /dev/null +++ b/mq/scripts/info.py @@ -0,0 +1,37 @@ +# coding=utf-8 +import json +import redis +from judge.judger_controller.settings import redis_config +from judge.judger.result import result +from submission.models import Submission +from problem.models import Problem + + +class MessageQueue(object): + def __init__(self): + self.conn = redis.StrictRedis(host=redis_config["host"], port=redis_config["port"], db=redis_config["db"]) + self.queue = 'queue' + + def listen_task(self): + while True: + submission_id = self.conn.blpop(self.queue, 0)[1] + print submission_id + try: + submission = Submission.objects.get(id=submission_id) + except Submission.DoesNotExist: + print "error 1" + pass + + 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: + print "error 2" + pass + + +print "mq running" +MessageQueue().listen_task() diff --git a/oj/settings.py b/oj/settings.py index 8d012d0..a8d1570 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -52,7 +52,10 @@ INSTALLED_APPS = ( 'problem', 'admin', 'submission', + 'mq', + + 'django_extensions', 'rest_framework', 'rest_framework_swagger', ) diff --git a/submission/views.py b/submission/views.py index 26e8d3a..e92716c 100644 --- a/submission/views.py +++ b/submission/views.py @@ -60,18 +60,6 @@ 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 From 40ed90885f022770c3b8c76a35a5c9c155c7690a Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 22 Aug 2015 14:12:58 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=20log=20?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/settings.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/oj/settings.py b/oj/settings.py index a8d1570..d1c10fd 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -129,10 +129,16 @@ LOGGING = { # 日志格式 }, 'handlers': { - 'file_handler': { + 'django_error': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', - 'filename': LOG_PATH + 'info.log', + 'filename': LOG_PATH + 'django.log', + 'formatter': 'standard' + }, + 'app_info': { + 'level': 'DEBUG', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': LOG_PATH + 'app_info.log', 'formatter': 'standard' }, 'console': { @@ -142,13 +148,13 @@ LOGGING = { } }, 'loggers': { - 'info_logger': { - 'handlers': ['file_handler', "console"], + 'app_info_logger': { + 'handlers': ['app_info', "console"], 'level': 'DEBUG', 'propagate': True }, 'django.request': { - 'handlers': ['file_handler', 'console'], + 'handlers': ['django_error', 'console'], 'level': 'DEBUG', 'propagate': True, }, From f86ebd8ba32b8ff578e3a8c33ec87e900e50ca80 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 22 Aug 2015 14:26:32 +0800 Subject: [PATCH 5/7] =?UTF-8?q?mq=20=E5=A2=9E=E5=8A=A0=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mq/scripts/info.py | 12 +++++++----- oj/settings.py | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mq/scripts/info.py b/mq/scripts/info.py index a4fa50e..b6482ed 100644 --- a/mq/scripts/info.py +++ b/mq/scripts/info.py @@ -1,11 +1,13 @@ # coding=utf-8 -import json +import logging import redis from judge.judger_controller.settings import redis_config from judge.judger.result import result from submission.models import Submission from problem.models import Problem +logger = logging.getLogger("app_info") + class MessageQueue(object): def __init__(self): @@ -15,11 +17,11 @@ class MessageQueue(object): def listen_task(self): while True: submission_id = self.conn.blpop(self.queue, 0)[1] - print submission_id + logger.debug("receive submission_id: " + submission_id) try: submission = Submission.objects.get(id=submission_id) except Submission.DoesNotExist: - print "error 1" + logger.warning("Submission does not exist, submission_id: " + submission_id) pass if submission.result == result["accepted"]: @@ -29,9 +31,9 @@ class MessageQueue(object): problem.total_accepted_number += 1 problem.save() except Problem.DoesNotExist: - print "error 2" + logger.warning("Submission problem does not exist, submission_id: " + submission_id) pass -print "mq running" +logger.debug("Start message queue") MessageQueue().listen_task() diff --git a/oj/settings.py b/oj/settings.py index ce699b4..5703566 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -148,7 +148,7 @@ LOGGING = { } }, 'loggers': { - 'app_info_logger': { + 'app_info': { 'handlers': ['app_info', "console"], 'level': 'DEBUG', 'propagate': True From 1fe35bd6e0ed529156141e3eaf8ca3c3dd25c8c0 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 22 Aug 2015 20:46:52 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E4=BA=86=E5=8D=95?= =?UTF-8?q?=E4=B8=AA=E6=AF=94=E8=B5=9B=E7=9A=84=E8=AF=A6=E6=83=85=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/models.py | 4 +- contest/serializers.py | 5 ++ contest/views.py | 66 +++++++++++++++++-- oj/urls.py | 4 +- .../src/js/app/oj/contest/contest_password.js | 24 +++++++ template/oj/contest/_contest_header.html | 38 +++++++++++ template/oj/contest/contest_index.html | 21 ++++++ template/oj/contest/contest_no_privilege.html | 26 ++++++++ 8 files changed, 178 insertions(+), 10 deletions(-) create mode 100644 static/src/js/app/oj/contest/contest_password.js create mode 100644 template/oj/contest/_contest_header.html create mode 100644 template/oj/contest/contest_index.html create mode 100644 template/oj/contest/contest_no_privilege.html diff --git a/contest/models.py b/contest/models.py index 0ece5ff..3611c3a 100644 --- a/contest/models.py +++ b/contest/models.py @@ -9,19 +9,17 @@ from group.models import Group class Contest(models.Model): title = models.CharField(max_length=40, unique=True) description = models.TextField() - # 比赛模式:0 即为是acm模式,1 即为是按照总的 ac 题目数量排名模式,2 即为按照 ac 的题目的总分排名模式 + # 比赛模式:0 即为是acm模式,1 即为是按照总的 ac 题目数量排名模式 mode = models.IntegerField() # 是否显示排名结果 show_rank = models.BooleanField() # 是否显示别人的提交记录 show_user_submission = models.BooleanField() - # 只能超级管理员创建公开赛,管理员只能创建小组内部的比赛 # 如果这一项不为空,即为有密码的公开赛,没有密码的可以为小组赛或者是公开赛(此时用比赛的类型来表示) password = models.CharField(max_length=30, blank=True, null=True) # 比赛的类型: 0 即为是小组赛,1 即为是无密码的公开赛,2 即为是有密码的公开赛 contest_type = models.IntegerField() - # 开始时间 start_time = models.DateTimeField() # 结束时间 diff --git a/contest/serializers.py b/contest/serializers.py index dd9f370..23d8474 100644 --- a/contest/serializers.py +++ b/contest/serializers.py @@ -103,3 +103,8 @@ class EditContestProblemSerializer(serializers.Serializer): sort_index = serializers.CharField(max_length=30) +class ContestPasswordVerifySerializer(serializers.Serializer): + contest_id = serializers.IntegerField() + password = serializers.CharField(max_length=30) + + diff --git a/contest/views.py b/contest/views.py index d00bee5..34271b1 100644 --- a/contest/views.py +++ b/contest/views.py @@ -9,15 +9,13 @@ from utils.shortcuts import (serializer_invalid_response, error_response, success_response, paginate, rand_str, error_page) from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN +from account.decorators import login_required from group.models import Group from .models import Contest, ContestProblem from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer, - CreateContestProblemSerializer, ContestProblemSerializer, EditContestProblemSerializer) - - -def contest_page(request, contest_id): - pass + CreateContestProblemSerializer, ContestProblemSerializer, + EditContestProblemSerializer, ContestPasswordVerifySerializer) class ContestAdminAPIView(APIView): @@ -220,4 +218,60 @@ class ContestProblemAdminAPIView(APIView): contest_problem = contest_problem.filter(Q(title__contains=keyword) | Q(description__contains=keyword)) - return paginate(request, contest_problem, ContestProblemSerializer) \ No newline at end of file + return paginate(request, contest_problem, ContestProblemSerializer) + + +class ContestPasswordVerifyAPIView(APIView): + @login_required + def post(self, request): + serializer = ContestPasswordVerifySerializer(data=request.data) + if serializer.is_valid(): + data = request.data + try: + contest = Contest.objects.get(id=data["contest_id"], contest_type=2) + except Contest.DoesNotExist: + return error_response(u"密码错误") + + if data["password"] != contest.password: + return error_response(u" 密码错误") + else: + print request.session.get("contests", None) + if "contests" not in request.session: + request.session["contests"] = [] + request.session["contests"].append(int(data["contest_id"])) + print request.session["contests"] + + return success_response(True) + else: + return serializer_invalid_response(serializer) + + +def check_user_contest_permission(request, contest): + # 有密码的公开赛 + if contest.contest_type == 2: + # 没有输入过密码 + if contest.id not in request.session.get("contests", []): + return {"result": False, "reason": "password_protect"} + + # 指定小组参加的 + if contest.contest_type == 0: + if not contest.groups.filter(id__in=request.user.group_set.all()).exists(): + return {"result": False, "reason": "limited_group"} + return {"result": True} + + +@login_required +def contest_page(request, contest_id): + print request.session.get("contests", None) + try: + contest = Contest.objects.get(id=contest_id) + except Contest.DoesNotExist: + return error_page(request, u"比赛不存在") + + Contest.objects.filter(Q(contest_type__in=[1, 2]) | Q(groups__in=request.user.group_set.all())) + + result = check_user_contest_permission(request, contest) + if not result["result"]: + return render(request, "oj/contest/contest_no_privilege.html", {"contenst": contest, "reason": result["reason"]}) + + return render(request, "oj/contest/contest_index.html", {"contest": contest}) \ No newline at end of file diff --git a/oj/urls.py b/oj/urls.py index 6dc7606..2f66950 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -8,7 +8,7 @@ from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterA UserAdminAPIView, UserInfoAPIView) from announcement.views import AnnouncementAdminAPIView -from contest.views import ContestAdminAPIView, ContestProblemAdminAPIView +from contest.views import ContestAdminAPIView, ContestProblemAdminAPIView, ContestPasswordVerifyAPIView from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView, JoinGroupAPIView, JoinGroupRequestAdminAPIView) @@ -64,5 +64,7 @@ urlpatterns = [ url(r'^api/submission/$', SubmissionAPIView.as_view(), name="submission_api"), url(r'^api/admin/submission/$', SubmissionAdminAPIView.as_view(), name="submission_admin_api_view"), url(r'^api/admin/monitor/$', QueueLengthMonitorAPIView.as_view(), name="queue_length_monitor_api"), + url(r'^contest/(?P\d+)/$', "contest.views.contest_page", name="contest_page"), + url(r'^api/contest/password/$', ContestPasswordVerifyAPIView.as_view(), name="contest_password_verify_api"), ] diff --git a/static/src/js/app/oj/contest/contest_password.js b/static/src/js/app/oj/contest/contest_password.js new file mode 100644 index 0000000..24d785f --- /dev/null +++ b/static/src/js/app/oj/contest/contest_password.js @@ -0,0 +1,24 @@ +require(["jquery", "bsAlert", "csrfToken"], function($, bsAlert, csrfTokenHeader){ + $("#contest-password-btn").click(function(){ + var password = $("#contest-password").val(); + if(!password){ + bsAlert("密码不能为空!"); + return; + } + $.ajax({ + beforeSend: csrfTokenHeader, + url: "/api/contest/password/", + data: {password: password, contest_id: location.href.split("/")[4]}, + method: "post", + dataType: "json", + success: function(data){ + if(!data.code){ + location.reload(); + } + else{ + bsAlert(data.data); + } + } + }) + }) +}); \ No newline at end of file diff --git a/template/oj/contest/_contest_header.html b/template/oj/contest/_contest_header.html new file mode 100644 index 0000000..62892ce --- /dev/null +++ b/template/oj/contest/_contest_header.html @@ -0,0 +1,38 @@ +{% load contest %} +

{{ contest.title }}

+ +
+
+ + + + + + + + + + + + + + + + {% ifequal contest.contest_type 0 %} + + {% endifequal %} + {% ifequal contest.contest_type 1 %} + + {% endifequal %} + {% ifequal contest.contest_type 2 %} + + {% endifequal %} + + + + +
开始时间结束时间状态比赛类型创建者
{{ contest.start_time }}{{ contest.end_time }}{{ contest|contest_status }}小组赛公开赛公开赛(密码保护){{ contest.created_by.username }}
+
+
{{ contest.description|safe }}
+
+

\ No newline at end of file diff --git a/template/oj/contest/contest_index.html b/template/oj/contest/contest_index.html new file mode 100644 index 0000000..7d32910 --- /dev/null +++ b/template/oj/contest/contest_index.html @@ -0,0 +1,21 @@ +{% extends 'oj_base.html' %} + +{% block body %} +
+ + {% include "oj/contest/_contest_header.html" %} +
+{% endblock %} \ No newline at end of file diff --git a/template/oj/contest/contest_no_privilege.html b/template/oj/contest/contest_no_privilege.html new file mode 100644 index 0000000..12c7fca --- /dev/null +++ b/template/oj/contest/contest_no_privilege.html @@ -0,0 +1,26 @@ +{% extends 'oj_base.html' %} + +{% block body %} +
+ + {% include "oj/contest/_contest_header.html" %} + {% ifequal reason "password_protect" %} +
+
+ + +
+ +
+ {% else %} + + {% endifequal %} +
+{% endblock %} +{% block js_block %} + +{% endblock %} \ No newline at end of file From c2a2035e1339a856c7f2797d94d2ec5ba2c02e79 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sat, 22 Aug 2015 20:54:32 +0800 Subject: [PATCH 7/7] fix typo --- contest/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contest/views.py b/contest/views.py index 4ddff2c..283f23e 100644 --- a/contest/views.py +++ b/contest/views.py @@ -267,7 +267,6 @@ def check_user_contest_permission(request, contest): @login_required def contest_page(request, contest_id): - print request.session.get("contests", None) try: contest = Contest.objects.get(id=contest_id) except Contest.DoesNotExist: @@ -277,7 +276,7 @@ def contest_page(request, contest_id): result = check_user_contest_permission(request, contest) if not result["result"]: - return render(request, "oj/contest/contest_no_privilege.html", {"contenst": contest, "reason": result["reason"]}) + return render(request, "oj/contest/contest_no_privilege.html", {"contest": contest, "reason": result["reason"]}) return render(request, "oj/contest/contest_index.html", {"contest": contest})