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] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=AF=94=E8=B5=9B=E7=9A=84?= =?UTF-8?q?=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 3611c3af..524f2eda 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 b6482edd..a092b0d2 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 00000000..d4f27bd8 --- /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 00000000..d6005aae --- /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 2cde917f..6bb5991a 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 e9f1052c..545c13af 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 ab8fb712..225da09a 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