From 4d701e0239628fbd587b5456ab8602942d50c251 Mon Sep 17 00:00:00 2001 From: hohoTT <609029365@qq.com> Date: Sun, 23 Aug 2015 18:25:28 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=89=8D=E5=8F=B0?= =?UTF-8?q?=E6=AF=94=E8=B5=9B=E9=A2=98=E7=9B=AE=E5=88=97=E8=A1=A8=E4=B8=8E?= =?UTF-8?q?=E5=8D=95=E4=B8=AA=E9=A2=98=E7=9B=AE=E7=9A=84=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/views.py | 34 +++++- oj/urls.py | 11 ++ submission/views.py | 17 +++ template/oj/contest/contest_problem.html | 100 ++++++++++++++++++ .../oj/contest/contest_problems_list.html | 64 +++++++++++ template/oj/contest/my_submissions_list.html | 52 +++++++++ template/oj/problem/problem_list.html | 2 +- utils/templatetags/problem.py | 8 +- 8 files changed, 280 insertions(+), 8 deletions(-) create mode 100644 template/oj/contest/contest_problem.html create mode 100644 template/oj/contest/contest_problems_list.html create mode 100644 template/oj/contest/my_submissions_list.html diff --git a/contest/views.py b/contest/views.py index fd816f6..924b0eb 100644 --- a/contest/views.py +++ b/contest/views.py @@ -263,6 +263,31 @@ def contest_page(request, contest_id): return render(request, "oj/contest/contest_index.html", {"contest": contest}) +def contest_problem_page(request, contest_id, contest_problem_id): + try: + Contest.objects.get(id=contest_id) + except Contest.DoesNotExist: + return error_page(request, u"比赛不存在") + try: + contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True) + except ContestProblem.DoesNotExist: + return error_page(request, u"比赛题目不存在") + return render(request, "oj/contest/contest_problem.html", {"contest_problem": contest_problem, + "samples": json.loads(contest_problem.samples)}) + + +@check_user_contest_permission +def contest_problems_list_page(request, contest_id): + try: + contest_problems = ContestProblem.objects.filter(contest=Contest.objects.get(id=contest_id)).order_by("sort_index") + except Contest.DoesNotExist: + return error_page(request, u"比赛题目不存在") + # 右侧的公告列表 + announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time") + return render(request, "oj/contest/contest_problems_list.html", {"contest_problems": contest_problems, + "announcements": announcements}) + + def contest_list_page(request, page=1): # 正常情况 contests = Contest.objects.filter(visible=True) @@ -298,10 +323,13 @@ def contest_list_page(request, page=1): # 右侧的公告列表 announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time") - # 系统当前时间 - now = datetime.datetime.now() + return render(request, "oj/contest/contest_list.html", {"contests": current_page, "page": int(page), "previous_page": previous_page, "next_page": next_page, "keyword": keyword, "announcements": announcements, - "join": join, "now": now}) \ No newline at end of file + "join": join}) + + + + diff --git a/oj/urls.py b/oj/urls.py index 08b0c93..805e9ec 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -40,7 +40,14 @@ urlpatterns = [ url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"), url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"), url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), + url(r'^problem/(?P\d+)/$', "problem.views.problem_page", name="problem_page"), + + url(r'^contest/(?P\d+)/problem/(?P\d+)/$', "contest.views.contest_problem_page", + name="contest_problem_page"), + url(r'^contest/(?P\d+)/$', "contest.views.contest_page", name="contest_page"), + url(r'^contest/(?P\d+)/problems/$', "contest.views.contest_problems_list_page", + name="contest_problems_list_page"), url(r'^announcement/(?P\d+)/$', "announcement.views.announcement_page", name="announcement_page"), url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), @@ -60,6 +67,10 @@ urlpatterns = [ url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"), url(r'^problem/(?P\d+)/my_submissions/$', "submission.views.problem_my_submissions_list_page", name="problem_my_submissions_page"), + + url(r'^contest/(?P\d+)/problem/(?P\d+)/my_submissions/$', + "submission.views.contest_problem_my_submissions_list_page", name="contest_problem_my_submissions_list_page"), + url(r'^my_submission/(?P\w+)/$', "submission.views.my_submission", name="my_submission_page"), url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), diff --git a/submission/views.py b/submission/views.py index ab8fb71..026b619 100644 --- a/submission/views.py +++ b/submission/views.py @@ -12,6 +12,7 @@ from judge.judger_controller.settings import redis_config from account.decorators import login_required from account.models import SUPER_ADMIN from problem.models import Problem +from contest.models import Contest, ContestProblem from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate from .models import Submission from .serializers import CreateSubmissionSerializer, SubmissionSerializer @@ -79,6 +80,22 @@ def problem_my_submissions_list_page(request, problem_id): {"submissions": submissions, "problem": problem}) +@login_required +def contest_problem_my_submissions_list_page(request, contest_id, contest_problem_id): + try: + Contest.objects.get(id=contest_id) + except Contest.DoesNotExist: + return error_page(request, u"比赛不存在") + try: + contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True) + except Problem.DoesNotExist: + return error_page(request, u"比赛问题不存在") + submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id).order_by("-create_time"). \ + values("id", "result", "create_time", "accepted_answer_time", "language") + return render(request, "oj/contest/my_submissions_list.html", + {"submissions": submissions, "contest_problem": contest_problem}) + + @login_required def my_submission(request, submission_id): try: diff --git a/template/oj/contest/contest_problem.html b/template/oj/contest/contest_problem.html new file mode 100644 index 0000000..69991ad --- /dev/null +++ b/template/oj/contest/contest_problem.html @@ -0,0 +1,100 @@ + +{% extends 'oj_base.html' %} + +{% block body %} +
+ +

{{ contest_problem.title }}

+ +

发布时间 : {{ contest_problem.create_time }}   + 时间限制 : {{ contest_problem.time_limit }}ms   + 内存限制 : {{ contest_problem.memory_limit }}M +

+ +
+
+ + +

{{ contest_problem.description|safe }}

+
+
+ + +

{{ contest_problem.input_description }}

+
+
+ + +

{{ contest_problem.output_description }}k

+
+ {% for item in samples %} +
+ +
+{{ item.input }}
+ +
+
+ + +
+{{ item.output }}
+
+ {% endfor %} + {% if problem.hint %} +
+ + +

{{ contest_problem.hint|safe }}

+
+ {% endif %} + {% if contest_problem.hint %} +
+ + +

{{ contest_problem.hint|safe }}

+
+ {% endif %} + +
+ + +
+ + + +
+
+ +
+ + +
+
+
+ + + +
+ +
+
+
+
+
+{% endblock %} +{% block js_block %} + +{% endblock %} \ No newline at end of file diff --git a/template/oj/contest/contest_problems_list.html b/template/oj/contest/contest_problems_list.html new file mode 100644 index 0000000..543135d --- /dev/null +++ b/template/oj/contest/contest_problems_list.html @@ -0,0 +1,64 @@ +{% extends "oj_base.html" %} +{% block body %} + {% load problem %} +
+
+
+
+
+
+
+ + +
+
+
+
+
+ + + + + + + + + + + {% for item in contest_problems %} + + + + + + + {% endfor %} + +
#题目通过率
{{ item.sort_index }}{{ item.title }}{{ item|accepted_radio }}
+ +
+
+ +
+ {% include "oj/announcement/_announcement_panel.html" %} +
+
+
+{% endblock %} + +{% block js_block %} + +{% endblock %} \ No newline at end of file diff --git a/template/oj/contest/my_submissions_list.html b/template/oj/contest/my_submissions_list.html new file mode 100644 index 0000000..dc4f5f5 --- /dev/null +++ b/template/oj/contest/my_submissions_list.html @@ -0,0 +1,52 @@ +{% extends 'oj_base.html' %} + +{% block body %} + + {% load submission %} +
+ +

{{ contest_problem.title }}

+ +

发布时间: {{ contest_problem.create_time }}   + 时间限制: {{ contest_problem.time_limit }}ms   + 内存限制: {{ contest_problem.memory_limit }}M

+ + + + + + + + + + + + {% for item in submissions %} + + + + + + + + {% endfor %} + + +
#提交时间结果运行时间语言
{{ forloop.counter }}{{ item.create_time }}{{ item.result|translate_result }} + {% if item.accepted_answer_time %} + {{ item.accepted_answer_time }}ms + {% else %} + -- + {% endif %} + + {{ item.language|translate_language }} +
+
+{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/problem_list.html b/template/oj/problem/problem_list.html index 21833a7..6c106e9 100644 --- a/template/oj/problem/problem_list.html +++ b/template/oj/problem/problem_list.html @@ -32,7 +32,7 @@ {{ item.id }} {{ item.title }} {{ item.difficulty }} - {{ item|accepted_radio }}% + {{ item|accepted_radio }} {% endfor %} diff --git a/utils/templatetags/problem.py b/utils/templatetags/problem.py index 70550f4..78ee4ea 100644 --- a/utils/templatetags/problem.py +++ b/utils/templatetags/problem.py @@ -2,13 +2,13 @@ def get_problem_accepted_radio(problem): - if problem.total_accepted_number: - return int((problem.total_accepted_number * 100) / problem.total_submit_number) - return 0 + if problem.total_submit_number: + return str(int((problem.total_accepted_number * 100) / problem.total_submit_number)) \ + + "% (" + str(problem.total_accepted_number) + "/" + str(problem.total_submit_number) + ")" + return "0%" from django import template - register = template.Library() register.filter("accepted_radio", get_problem_accepted_radio) From 7a22d7863133b8bf4c4cd4ccb0e923efddc3ce44 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 23 Aug 2015 18:28:30 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=AF=94=E8=B5=9B?= =?UTF-8?q?=E7=9A=84=E5=88=A4=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/models.py | 17 +++++ mq/scripts/info.py | 69 +++++++++++++++++-- .../0004_remove_submission_is_counted.py | 18 +++++ .../migrations/0005_submission_contest_id.py | 19 +++++ submission/models.py | 2 +- submission/serializers.py | 9 ++- submission/views.py | 52 ++++++++++++-- 7 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 submission/migrations/0004_remove_submission_is_counted.py create mode 100644 submission/migrations/0005_submission_contest_id.py diff --git a/contest/models.py b/contest/models.py index 3611c3a..524f2ed 100644 --- a/contest/models.py +++ b/contest/models.py @@ -58,3 +58,20 @@ class ContestProblemTestCase(models.Model): class Meta: db_table = "contest_problem_test_case" + + +class ContestSubmission(models.Model): + """ + 用于保存比赛提交和排名的一些数据,加快检索速度 + """ + user = models.ForeignKey(User) + problem = models.ForeignKey(ContestProblem) + contest = models.ForeignKey(Contest) + total_submission_number = models.IntegerField(default=1) + # 这道题是 AC 还是没过 + ac = models.BooleanField() + # 总的时间,用于acm 类型的,也需要保存罚时 + total_time = models.IntegerField(default=0) + + class Meta: + db_table = "contest_submission" diff --git a/mq/scripts/info.py b/mq/scripts/info.py index b6482ed..a092b0d 100644 --- a/mq/scripts/info.py +++ b/mq/scripts/info.py @@ -1,10 +1,13 @@ # coding=utf-8 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 +from contest.models import ContestProblem, Contest, ContestSubmission logger = logging.getLogger("app_info") @@ -22,17 +25,75 @@ class MessageQueue(object): submission = Submission.objects.get(id=submission_id) except Submission.DoesNotExist: logger.warning("Submission does not exist, submission_id: " + submission_id) - pass + continue - if submission.result == result["accepted"]: - # 更新题目的 ac 计数器 + if submission.result == result["accepted"] and not submission.contest_id: + # 更新普通题目的 ac 计数器 try: problem = Problem.objects.get(id=submission.problem_id) problem.total_accepted_number += 1 problem.save() except Problem.DoesNotExist: logger.warning("Submission problem does not exist, submission_id: " + submission_id) - pass + # 普通题目的话,到这里就结束了 + continue + + # 能运行到这里的都是比赛题目 + try: + contest = Contest.objects.get(id=submission.contest_id) + 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) + continue + except ContestProblem.DoesNotExist: + logger.warning("Submission problem does not exist, submission_id: " + submission_id) + continue + + try: + contest_submission = ContestSubmission.objects.get(user_id=submission.user_id, contest=contest, + problem_id=contest_problem.id) + # 如果这道题已经有提交记录了,总的提交次数计数器加1 + contest_submission.total_submission_number += 1 + + if submission.result == result["accepted"]: + + # 避免这道题已经 ac 了,但是又重新提交了一遍 + if not contest_submission.result: + # 这种情况是这个题目前处于错误状态,就使用已经存储了的罚时加上这道题的实际用时 + contest_submission.total_time += int((submission.create_time - contest.start_time).seconds / 60) + # 标记为已经通过 + contest_submission.ac = True + # contest problem ac 计数器加1 + contest_problem.total_accepted_number += 1 + else: + # 如果这个提交是错误的,就罚时20分钟 + contest_submission.ac = False + contest_submission.total_time += 20 + contest_submission.save() + contest_problem.save() + except ContestSubmission.DoesNotExist: + # 第一次提交 + is_ac = submission.result == result["accepted"] + # 增加题目总提交数计数器 + contest_problem.total_submit_number += 1 + if is_ac: + total_time = 0 + # 增加题目总的ac数计数器 + contest_problem.total_accepted_number += 1 + contest_problem.save() + else: + # 没过罚时20分钟 + total_time = 20 + ContestSubmission.objects.create(user_id=submission.user_id, contest=contest, problem=contest_problem, + ac=is_ac, total_time=total_time) + + + + + + + except ContestSubmission.DoesNotExist: + pass logger.debug("Start message queue") diff --git a/submission/migrations/0004_remove_submission_is_counted.py b/submission/migrations/0004_remove_submission_is_counted.py new file mode 100644 index 0000000..d4f27bd --- /dev/null +++ b/submission/migrations/0004_remove_submission_is_counted.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('submission', '0003_auto_20150821_1654'), + ] + + operations = [ + migrations.RemoveField( + model_name='submission', + name='is_counted', + ), + ] diff --git a/submission/migrations/0005_submission_contest_id.py b/submission/migrations/0005_submission_contest_id.py new file mode 100644 index 0000000..d6005aa --- /dev/null +++ b/submission/migrations/0005_submission_contest_id.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', '0004_remove_submission_is_counted'), + ] + + operations = [ + migrations.AddField( + model_name='submission', + name='contest_id', + field=models.IntegerField(null=True, blank=True), + ), + ] diff --git a/submission/models.py b/submission/models.py index 2cde917..6bb5991 100644 --- a/submission/models.py +++ b/submission/models.py @@ -11,6 +11,7 @@ class Submission(models.Model): result = models.IntegerField(default=result["waiting"]) language = models.IntegerField() code = models.TextField() + contest_id = models.IntegerField(blank=True, null=True) problem_id = models.IntegerField(db_index=True) # 这个字段可能存储很多数据 比如编译错误、系统错误的时候,存储错误原因字符串 # 正常运行的时候存储 lrun 的判题结果,比如cpu时间内存之类的 @@ -18,7 +19,6 @@ 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/serializers.py b/submission/serializers.py index e9f1052..545c13a 100644 --- a/submission/serializers.py +++ b/submission/serializers.py @@ -19,4 +19,11 @@ class SubmissionSerializer(serializers.ModelSerializer): fields = ["id", "result", "create_time", "language", "user"] def _get_submission_user(self, obj): - return User.objects.get(id=obj.user_id).username \ No newline at end of file + return User.objects.get(id=obj.user_id).username + + +class CreateContestSubmissionSerializer(serializers.Serializer): + contest_id = serializers.IntegerField() + problem_id = serializers.IntegerField() + language = serializers.IntegerField() + code = serializers.CharField(max_length=3000) \ No newline at end of file diff --git a/submission/views.py b/submission/views.py index ab8fb71..225da09 100644 --- a/submission/views.py +++ b/submission/views.py @@ -1,21 +1,22 @@ # coding=utf-8 import json -import redis +import redis from django.shortcuts import render +from django.core.paginator import Paginator from rest_framework.views import APIView -from judge.judger.result import result +from problem.models import Problem 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 problem.models import Problem +from contest.models import Contest, ContestProblem +from contest.decorators import check_user_contest_permission from utils.shortcuts import serializer_invalid_response, error_response, success_response, error_page, paginate from .models import Submission -from .serializers import CreateSubmissionSerializer, SubmissionSerializer -from django.core.paginator import Paginator +from .serializers import CreateSubmissionSerializer, SubmissionSerializer, CreateContestSubmissionSerializer class SubmissionAPIView(APIView): @@ -136,3 +137,44 @@ def my_submission_list_page(request, page=1): 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}) + + + +class ContestSubmissionAPIView(APIView): + @check_user_contest_permission + def post(self, request): + """ + 创建比赛的提交 + --- + request_serializer: ConestSubmissionSerializer + """ + serializer = CreateContestSubmissionSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + try: + contest = Contest.objects.get(id=data["contest_id"]) + except Contest.DoesNotExist: + return error_response(u"比赛不存在") + try: + problem = ContestProblem.objects.get(contest=contest, 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"]), + contest_id=contest.id, code=data["code"], problem_id=problem.id) + try: + judge.delay(submission.id, problem.time_limit, problem.memory_limit, problem.test_case_id) + except Exception: + 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") + + return success_response({"submission_id": submission.id}) + + else: + return serializer_invalid_response(serializer) \ No newline at end of file From 5df7ba41267eca6dc0a7c8bb5e195215a96cb124 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 23 Aug 2015 19:27:31 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=AF=94=E8=B5=9B?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/migrations/0005_contestsubmission.py | 31 ++++++++++++++ contest/views.py | 3 +- mq/scripts/info.py | 17 +++----- static/src/js/app/oj/problem/problem.js | 43 ++++++++++++++------ 4 files changed, 68 insertions(+), 26 deletions(-) create mode 100644 contest/migrations/0005_contestsubmission.py diff --git a/contest/migrations/0005_contestsubmission.py b/contest/migrations/0005_contestsubmission.py new file mode 100644 index 0000000..f1018d8 --- /dev/null +++ b/contest/migrations/0005_contestsubmission.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('contest', '0004_remove_contestproblem_difficulty'), + ] + + operations = [ + migrations.CreateModel( + name='ContestSubmission', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('total_submission_number', models.IntegerField(default=1)), + ('ac', models.BooleanField()), + ('total_time', models.IntegerField(default=0)), + ('contest', models.ForeignKey(to='contest.Contest')), + ('problem', models.ForeignKey(to='contest.ContestProblem')), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'contest_submission', + }, + ), + ] diff --git a/contest/views.py b/contest/views.py index 924b0eb..ae288cc 100644 --- a/contest/views.py +++ b/contest/views.py @@ -285,7 +285,8 @@ def contest_problems_list_page(request, contest_id): # 右侧的公告列表 announcements = Announcement.objects.filter(is_global=True, visible=True).order_by("-create_time") return render(request, "oj/contest/contest_problems_list.html", {"contest_problems": contest_problems, - "announcements": announcements}) + "announcements": announcements, + "contest": {"id": contest_id}}) def contest_list_page(request, page=1): diff --git a/mq/scripts/info.py b/mq/scripts/info.py index a092b0d..70843d0 100644 --- a/mq/scripts/info.py +++ b/mq/scripts/info.py @@ -52,15 +52,16 @@ class MessageQueue(object): try: contest_submission = ContestSubmission.objects.get(user_id=submission.user_id, contest=contest, problem_id=contest_problem.id) - # 如果这道题已经有提交记录了,总的提交次数计数器加1 - contest_submission.total_submission_number += 1 if submission.result == result["accepted"]: # 避免这道题已经 ac 了,但是又重新提交了一遍 - if not contest_submission.result: + if not contest_submission.ac: # 这种情况是这个题目前处于错误状态,就使用已经存储了的罚时加上这道题的实际用时 - contest_submission.total_time += int((submission.create_time - contest.start_time).seconds / 60) + logger.debug(contest.start_time) + logger.debug(submission.create_time) + logger.debug((submission.create_time - contest.start_time).total_seconds()) + contest_submission.total_time += int((submission.create_time - contest.start_time).total_seconds() / 60) # 标记为已经通过 contest_submission.ac = True # contest problem ac 计数器加1 @@ -88,13 +89,5 @@ class MessageQueue(object): ac=is_ac, total_time=total_time) - - - - - except ContestSubmission.DoesNotExist: - pass - - logger.debug("Start message queue") MessageQueue().listen_task() diff --git a/static/src/js/app/oj/problem/problem.js b/static/src/js/app/oj/problem/problem.js index 009929b..d936e0f 100644 --- a/static/src/js/app/oj/problem/problem.js +++ b/static/src/js/app/oj/problem/problem.js @@ -56,7 +56,7 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro var counter = 0; function getResult() { - if(counter++ > 10){ + if (counter++ > 10) { hideLoading(); bsAlert("抱歉,服务器可能出现了故障,请稍后到我的提交列表中查看"); counter = 0; @@ -88,12 +88,32 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro } $("#submit-code-button").click(function () { - var problemId = window.location.pathname.split("/")[2]; + var code = codeEditor.getValue(); + if (location.href.indexOf("contest") > -1) { + var problemId = location.pathname.split("/")[4]; + var contestId = location.pathname.split("/")[2]; + var url = "/api/contest/submission/"; + var data = { + problem_id: problemId, + language: language, + code: code, + contest_id: contestId + }; + } + else { + var problemId = window.location.pathname.split("/")[2]; + var url = "/api/submission/"; + var data = { + problem_id: problemId, + language: language, + code: code + }; + } showLoading(); - if(!code.trim()){ + if (!code.trim()) { bsAlert("请填写代码!"); hideLoading(); return false; @@ -101,15 +121,12 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro $("#result").html(""); + $.ajax({ beforeSend: csrfTokenHeader, - url: "/api/submission/", + url: url, method: "post", - data: JSON.stringify({ - problem_id: problemId, - language: language, - code: codeEditor.getValue() - }), + data: JSON.stringify(data), contentType: "application/json", success: function (data) { if (!data.code) { @@ -118,7 +135,7 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro setTimeout(getResult, 2000); } else { - bs_alert(data.data); + bsAlert(data.data); hideLoading(); } } @@ -127,11 +144,11 @@ require(["jquery", "codeMirror", "csrfToken", "bsAlert"], function ($, codeMirro }); $.ajax({ - url : "/api/user/", + url: "/api/user/", method: "get", dataType: "json", - success: function(data){ - if(data.code){ + success: function (data) { + if (data.code) { $("#submit-code-button").attr("disabled", "disabled"); $("#result").html(''); } From 4a3a323769d77287e8540370a0cd69aeacbf7a56 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 23 Aug 2015 19:39:57 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=A2=98=E7=9B=AE?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E5=92=8C=E9=A6=96=E9=A1=B5=E7=9A=84=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/css/oj.css | 4 ++ .../oj/contest/contest_problems_list.html | 43 +++++++++++++------ template/oj/index.html | 2 +- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/static/src/css/oj.css b/static/src/css/oj.css index 95a16f8..ce1bc0e 100644 --- a/static/src/css/oj.css +++ b/static/src/css/oj.css @@ -87,4 +87,8 @@ li.list-group-item { position: relative; outline: none; +} + +.contest-tab{ + margin-bottom: 5px; } \ No newline at end of file diff --git a/template/oj/contest/contest_problems_list.html b/template/oj/contest/contest_problems_list.html index 543135d..1b40095 100644 --- a/template/oj/contest/contest_problems_list.html +++ b/template/oj/contest/contest_problems_list.html @@ -2,18 +2,31 @@ {% block body %} {% load problem %}
+ +
+
+ + + + +
+
-
-
-
-
- - -
-
-
-
+
@@ -28,8 +41,11 @@ {% for item in contest_problems %} - - + + {% endfor %} @@ -51,10 +67,11 @@ -
{% include "oj/announcement/_announcement_panel.html" %}
+ + {% endblock %} diff --git a/template/oj/index.html b/template/oj/index.html index 67610b9..520f753 100644 --- a/template/oj/index.html +++ b/template/oj/index.html @@ -90,7 +90,7 @@
From 435ae9ddfc178fff1f0ca10582743e6227faa708 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 23 Aug 2015 19:40:09 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=AF=94=E8=B5=9B?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E7=9A=84=20url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oj/urls.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/oj/urls.py b/oj/urls.py index 805e9ec..c1b3a57 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -16,7 +16,7 @@ from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView, from admin.views import AdminTemplateView from problem.views import TestCaseUploadAPIView, ProblemTagAdminAPIView, ProblemAdminAPIView -from submission.views import SubmissionAPIView, SubmissionAdminAPIView +from submission.views import SubmissionAPIView, SubmissionAdminAPIView, ContestSubmissionAPIView from monitor.views import QueueLengthMonitorAPIView @@ -82,5 +82,6 @@ urlpatterns = [ 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"), + url(r'^api/contest/submission/$', ContestSubmissionAPIView.as_view(), name="contest_submission_api"), ] From b590ee85776fbec404624eabb06e6c8fbfa7eaa6 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 23 Aug 2015 20:37:40 +0800 Subject: [PATCH 6/8] =?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 bc95cf5..0a05762 100644 --- a/static/src/js/app/oj/account/login.js +++ b/static/src/js/app/oj/account/login.js @@ -14,8 +14,15 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c //成功登陆 var ref = document.referrer; if(ref){ - if(ref.split("/")[2] == location.hostname){ + // 注册页和本页的来源的跳转回首页,防止死循环 + if(ref.indexOf("register") > -1 || ref.indexOf("login") > -1){ + location.href = "/"; + return; + } + // 判断来源,只有同域下才跳转 + if(ref.split("/")[2].split(":")[0] == location.hostname){ location.href = ref; + return; } } location.href = "/"; From f682aa1fb3cf4b7aab8aaee77b6bdca67658ce0d Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 23 Aug 2015 20:45:02 +0800 Subject: [PATCH 7/8] =?UTF-8?q?=E6=B2=A1=E6=9C=89=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E7=9A=84=E6=97=B6=E5=80=99=E8=87=AA=E5=8A=A8=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E5=88=B0=E7=99=BB=E5=BD=95=E9=A1=B5=E9=9D=A2=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E7=9B=B8=E5=85=B3=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/decorators.py | 6 +++--- account/test_urls.py | 2 ++ account/tests.py | 6 +++--- contest/decorators.py | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/account/decorators.py b/account/decorators.py index 33634e5..4113dd0 100644 --- a/account/decorators.py +++ b/account/decorators.py @@ -1,6 +1,6 @@ # coding=utf-8 from functools import wraps -from django.http import HttpResponse +from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render from utils.shortcuts import error_response, error_page @@ -18,7 +18,7 @@ def login_required(func): if request.is_ajax(): return error_response(u"请先登录") else: - return error_page(request, u"请先登录") + return HttpResponseRedirect("/login/") return check @@ -31,5 +31,5 @@ def admin_required(func): if request.is_ajax(): return error_response(u"需要管理员权限") else: - return error_page(request, u"需要管理员权限") + return error_page(request, u"需要管理员权限,如果没有登录,请先登录") return check diff --git a/account/test_urls.py b/account/test_urls.py index 9757d90..5d2bdcc 100644 --- a/account/test_urls.py +++ b/account/test_urls.py @@ -1,5 +1,6 @@ # coding=utf-8 from django.conf.urls import include, url +from django.views.generic import TemplateView from .tests import (LoginRequiredCBVTestWithArgs, LoginRequiredCBVTestWithoutArgs, AdminRequiredCBVTestWithArgs, AdminRequiredCBVTestWithoutArgs) @@ -15,4 +16,5 @@ urlpatterns = [ url(r'^admin_required_test/fbv/(?P\d+)/$', "account.tests.admin_required_FBC_test_with_args"), url(r'^admin_required_test/cbv/1/$', AdminRequiredCBVTestWithoutArgs.as_view()), url(r'^admin_required_test/cbv/(?P\d+)/$', AdminRequiredCBVTestWithArgs.as_view()), + url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"), ] diff --git a/account/tests.py b/account/tests.py index a84911c..0b7a8bb 100644 --- a/account/tests.py +++ b/account/tests.py @@ -260,7 +260,7 @@ class LoginRequiredDecoratorTest(TestCase): def test_fbv_without_args(self): # 没登陆 response = self.client.get("/login_required_test/fbv/1/") - self.assertTemplateUsed(response, "utils/error.html") + self.assertRedirects(response, "/login/") # 登陆后 self.client.login(username="test", password="test") @@ -270,7 +270,7 @@ class LoginRequiredDecoratorTest(TestCase): def test_fbv_with_args(self): # 没登陆 response = self.client.get("/login_required_test/fbv/1024/") - self.assertTemplateUsed(response, "utils/error.html") + self.assertRedirects(response, "/login/") # 登陆后 self.client.login(username="test", password="test") @@ -353,7 +353,7 @@ class AdminRequiredDecoratorTest(TestCase): def test_cbv_without_args(self): # 没登陆 response = self.client.get("/admin_required_test/cbv/1/") - self.assertTemplateUsed(response, "utils/error.html") + self.assertRedirects(response, "/login/") # 登陆后 self.client.login(username="test", password="test") diff --git a/contest/decorators.py b/contest/decorators.py index 06a8b70..0b25759 100644 --- a/contest/decorators.py +++ b/contest/decorators.py @@ -1,7 +1,7 @@ # coding=utf-8 from functools import wraps -from django.http import HttpResponse +from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render from django.utils.timezone import now @@ -30,7 +30,7 @@ def check_user_contest_permission(func): if request.is_ajax(): return error_response(u"请先登录") else: - return error_page(request, u"请先登录") + return HttpResponseRedirect("/login/") # kwargs 就包含了url 里面的播或参数 if "contest_id" in kwargs: From f58c1e6d9e920d8b2a7f8a7c77328d9550633bfe Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Sun, 23 Aug 2015 20:45:51 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contest/views.py | 14 +++++++++++--- submission/views.py | 5 ++--- template/oj/contest/contest_problem.html | 4 ++++ template/oj/contest/contest_problems_list.html | 13 +++++++------ template/oj/contest/problems.html | 15 --------------- template/oj/problem/problem_list.html | 6 +++--- 6 files changed, 27 insertions(+), 30 deletions(-) delete mode 100644 template/oj/contest/problems.html diff --git a/contest/views.py b/contest/views.py index ae288cc..42ecd62 100644 --- a/contest/views.py +++ b/contest/views.py @@ -17,7 +17,7 @@ from account.decorators import login_required from group.models import Group from announcement.models import Announcement -from .models import Contest, ContestProblem +from .models import Contest, ContestProblem, ContestSubmission from .decorators import check_user_contest_permission from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer, CreateContestProblemSerializer, ContestProblemSerializer, @@ -263,17 +263,25 @@ def contest_page(request, contest_id): return render(request, "oj/contest/contest_index.html", {"contest": contest}) +@check_user_contest_permission def contest_problem_page(request, contest_id, contest_problem_id): try: - Contest.objects.get(id=contest_id) + contest = Contest.objects.get(id=contest_id) except Contest.DoesNotExist: return error_page(request, u"比赛不存在") try: contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True) except ContestProblem.DoesNotExist: return error_page(request, u"比赛题目不存在") + show_warning = False + try: + submission = ContestSubmission.objects.get(user=request.user, contest=contest, problem=contest_problem) + show_warning = submission.ac + except ContestSubmission.DoesNotExist: + pass return render(request, "oj/contest/contest_problem.html", {"contest_problem": contest_problem, - "samples": json.loads(contest_problem.samples)}) + "samples": json.loads(contest_problem.samples), + "show_warning": show_warning}) @check_user_contest_permission diff --git a/submission/views.py b/submission/views.py index eb4a3bb..6399e22 100644 --- a/submission/views.py +++ b/submission/views.py @@ -93,8 +93,7 @@ def contest_problem_my_submissions_list_page(request, contest_id, contest_proble contest_problem = ContestProblem.objects.get(id=contest_problem_id, visible=True) except Problem.DoesNotExist: return error_page(request, u"比赛问题不存在") - submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id).order_by( - "-create_time"). \ + submissions = Submission.objects.filter(user_id=request.user.id, problem_id=contest_problem.id).order_by("-create_time"). \ values("id", "result", "create_time", "accepted_answer_time", "language") return render(request, "oj/contest/my_submissions_list.html", {"submissions": submissions, "contest_problem": contest_problem}) @@ -160,7 +159,7 @@ def my_submission_list_page(request, page=1): class ContestSubmissionAPIView(APIView): - # @check_user_contest_permission + @check_user_contest_permission def post(self, request): """ 创建比赛的提交 diff --git a/template/oj/contest/contest_problem.html b/template/oj/contest/contest_problem.html index 69991ad..32b0617 100644 --- a/template/oj/contest/contest_problem.html +++ b/template/oj/contest/contest_problem.html @@ -81,6 +81,7 @@

+
+ {% if show_warning %} + + {% endif %}
diff --git a/template/oj/contest/contest_problems_list.html b/template/oj/contest/contest_problems_list.html index 1b40095..23cefd7 100644 --- a/template/oj/contest/contest_problems_list.html +++ b/template/oj/contest/contest_problems_list.html @@ -21,8 +21,6 @@ 排名 - -
@@ -40,11 +38,14 @@
{% for item in contest_problems %} - - - + diff --git a/template/oj/contest/problems.html b/template/oj/contest/problems.html deleted file mode 100644 index 0f4e49e..0000000 --- a/template/oj/contest/problems.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "oj_base.html" %} -{% block body %} -
- -

第一次比赛

- -

开始时间: 2015-6-8 19:00 结束时间: 2015-9-1 12:00

- -
-{% endblock %} \ No newline at end of file diff --git a/template/oj/problem/problem_list.html b/template/oj/problem/problem_list.html index 6c106e9..a26ae80 100644 --- a/template/oj/problem/problem_list.html +++ b/template/oj/problem/problem_list.html @@ -22,15 +22,15 @@ - + {% for item in problems %} - - + +
{{ item.sort_index }}{{ item.title }}{{ item.sort_index }} + {{ item.title }} + {{ item|accepted_radio }}
{{ item.sort_index }} + + {{ item.title }} + + {{ item.sort_index }} + + {{ item.title }} {{ item|accepted_radio }}
# 题目 难度通过率通过率
{{ item.id }}{{ item.title }}{{ item.id }}{{ item.title }} {{ item.difficulty }} {{ item|accepted_radio }}