From edceada9a2e56ad50ba40c19d35a9f270eb1690c Mon Sep 17 00:00:00 2001 From: HandsomeHow <673712786@qq.com> Date: Wed, 5 Dec 2018 19:55:17 +0800 Subject: [PATCH 1/8] add sim binary and modify contest models --- .../0010_contest_similarity_check_result.py | 21 ++++++++++++++++++ contest/models.py | 1 + sim_c | Bin 0 -> 59208 bytes 3 files changed, 22 insertions(+) create mode 100644 contest/migrations/0010_contest_similarity_check_result.py create mode 100755 sim_c diff --git a/contest/migrations/0010_contest_similarity_check_result.py b/contest/migrations/0010_contest_similarity_check_result.py new file mode 100644 index 000000000..e4c2a5bb2 --- /dev/null +++ b/contest/migrations/0010_contest_similarity_check_result.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-12-05 10:13 +from __future__ import unicode_literals + +import django.contrib.postgres.fields.jsonb +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('contest', '0009_auto_20180501_0436'), + ] + + operations = [ + migrations.AddField( + model_name='contest', + name='similarity_check_result', + field=django.contrib.postgres.fields.jsonb.JSONField(default=list), + ), + ] diff --git a/contest/models.py b/contest/models.py index 77dcf8e21..c7cde62ea 100644 --- a/contest/models.py +++ b/contest/models.py @@ -24,6 +24,7 @@ class Contest(models.Model): # 是否可见 false的话相当于删除 visible = models.BooleanField(default=True) allowed_ip_ranges = JSONField(default=list) + similarity_check_result = JSONField(default=list) @property def status(self): diff --git a/sim_c b/sim_c new file mode 100755 index 0000000000000000000000000000000000000000..63cc858052cbeaea5890c3bd6a679b170782f5be GIT binary patch literal 59208 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 literal 0 HcmV?d00001 From 9fa8b62315092e84870d986e335ded5bb6a2a4d8 Mon Sep 17 00:00:00 2001 From: HandsomeHow <673712786@qq.com> Date: Fri, 7 Dec 2018 12:20:40 +0800 Subject: [PATCH 2/8] add interface for similiar check --- contest/urls/admin.py | 3 ++- contest/urls/oj.py | 2 ++ contest/views/admin.py | 7 +++++++ contest/views/oj.py | 5 +++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/contest/urls/admin.py b/contest/urls/admin.py index 0b017cc80..f82e697bf 100644 --- a/contest/urls/admin.py +++ b/contest/urls/admin.py @@ -1,10 +1,11 @@ from django.conf.urls import url -from ..views.admin import ContestAnnouncementAPI, ContestAPI, ACMContestHelper, DownloadContestSubmissions +from ..views.admin import ContestAnnouncementAPI, ContestAPI, ACMContestHelper, DownloadContestSubmissions, ContestCheckSimiliarAPI urlpatterns = [ url(r"^contest/?$", ContestAPI.as_view(), name="contest_admin_api"), url(r"^contest/announcement/?$", ContestAnnouncementAPI.as_view(), name="contest_announcement_admin_api"), url(r"^contest/acm_helper/?$", ACMContestHelper.as_view(), name="acm_contest_helper"), url(r"^download_submissions/?$", DownloadContestSubmissions.as_view(), name="acm_contest_helper"), + url(r"^contesteck_similiar/?$", ContestCheckSimiliarAPI.as_view(), name="contest_check_similiar_admin_api") ] diff --git a/contest/urls/oj.py b/contest/urls/oj.py index 9e94fa58f..c0e09afb8 100644 --- a/contest/urls/oj.py +++ b/contest/urls/oj.py @@ -4,6 +4,7 @@ from ..views.oj import ContestPasswordVerifyAPI, ContestAccessAPI from ..views.oj import ContestListAPI, ContestAPI from ..views.oj import ContestRankAPI +from ..views.oj import ContestGetSimiliarAPI urlpatterns = [ url(r"^contests/?$", ContestListAPI.as_view(), name="contest_list_api"), @@ -12,4 +13,5 @@ url(r"^contest/announcement/?$", ContestAnnouncementListAPI.as_view(), name="contest_announcement_api"), url(r"^contest/access/?$", ContestAccessAPI.as_view(), name="contest_access_api"), url(r"^contest_rank/?$", ContestRankAPI.as_view(), name="contest_rank_api"), + url(r"^contest/get_similiar/?$", ContestGetSimiliarAPI.as_view(), name="contest_get_similiar_api"), ] diff --git a/contest/views/admin.py b/contest/views/admin.py index 9adb45adf..954393869 100644 --- a/contest/views/admin.py +++ b/contest/views/admin.py @@ -19,6 +19,7 @@ CreateConetestSeriaizer, CreateContestAnnouncementSerializer, EditConetestSeriaizer, EditContestAnnouncementSerializer, ACMContesHelperSerializer, ) +from account.decorators import super_admin_required class ContestAPI(APIView): @@ -239,3 +240,9 @@ def get(self, request): resp["Content-Type"] = "application/zip" resp["Content-Disposition"] = f"attachment;filename={os.path.basename(zip_path)}" return resp + + +class ContestCheckSimiliarAPI(APIView): + @super_admin_required + def get(self, request): + return self.success() diff --git a/contest/views/oj.py b/contest/views/oj.py index 985507faf..77827cc1b 100644 --- a/contest/views/oj.py +++ b/contest/views/oj.py @@ -185,3 +185,8 @@ def get(self, request): page_qs = self.paginate_data(request, qs) page_qs["results"] = serializer(page_qs["results"], many=True, is_contest_admin=is_contest_admin).data return self.success(page_qs) + + +class ContestGetSimiliarAPI(APIView): + def get(self, request): + return self.success() From c9aaa8e417b87df83aa621e9df77c958aac06891 Mon Sep 17 00:00:00 2001 From: HandsomeHow <673712786@qq.com> Date: Fri, 7 Dec 2018 20:19:06 +0800 Subject: [PATCH 3/8] similarity check finished --- contest/tasks.py | 57 ++++++++++++++++++++++++++++++++++++++++++ contest/views/admin.py | 3 +++ 2 files changed, 60 insertions(+) create mode 100644 contest/tasks.py diff --git a/contest/tasks.py b/contest/tasks.py new file mode 100644 index 000000000..880780014 --- /dev/null +++ b/contest/tasks.py @@ -0,0 +1,57 @@ +from celery import shared_task + +from submission.models import Submission + +import os +from django.conf import settings +from contest.models import Contest +from problem.models import Problem +from submission.models import JudgeStatus + + +@shared_task(time_limit=30) +def similiar_task(contest_id): + problems = Problem.objects.filter(contest_id=contest_id) + owner = {} + data_to_write = [] + + for problem in problems: + check_dir = os.path.join(settings.TEST_CASE_DIR, str(problem.id) + "_similiar_tmp") + if not os.path.exists(check_dir): + os.mkdir(check_dir) + os.chmod(check_dir, 0o710) + + submissions = Submission.objects.filter(problem_id=problem.id, result=JudgeStatus.ACCEPTED) + for submission in submissions: + owner[submission.id] = submission.username + file_path = os.path.join(check_dir, submission.id) + f = open(file_path, "w") + f.write(submission.code) + f.close() + + output_dir = os.path.join(check_dir, "result") + check_files = check_dir + "/*" + os.system(f"/app/sim_c -p -t40 -o {output_dir} {check_files}") + f = open(output_dir) + for line in f: + # xxxx consists for xx % of xxxx material + if "consists" in line: + splited_line = line.split() + sub1 = splited_line[0].split("/")[-1] + sub2 = splited_line[-2].split("/")[-1] + if owner[sub1] == owner[sub2]: + continue + sim = splited_line[3] + data_to_write.append("#".join([problem._id, sub1, owner[sub1], sub2, owner[sub2], sim])) + f.close() + + os.remove(output_dir) + for submission in submissions: + owner[submission.id] = submission.username + file_path = os.path.join(check_dir, submission.id) + os.remove(file_path) + os.rmdir(check_dir) + + contest = Contest.objects.get(id=contest_id) + contest.similarity_check_result = data_to_write + contest.save() diff --git a/contest/views/admin.py b/contest/views/admin.py index 954393869..232b3e608 100644 --- a/contest/views/admin.py +++ b/contest/views/admin.py @@ -20,6 +20,7 @@ EditConetestSeriaizer, EditContestAnnouncementSerializer, ACMContesHelperSerializer, ) from account.decorators import super_admin_required +from contest.tasks import similiar_task class ContestAPI(APIView): @@ -245,4 +246,6 @@ def get(self, request): class ContestCheckSimiliarAPI(APIView): @super_admin_required def get(self, request): + cid = request.GET.get("contest_id") + similiar_task.delay(cid) return self.success() From ceea8a0415eb1b8e4001de39f72e391c60955369 Mon Sep 17 00:00:00 2001 From: HandsomeHow <673712786@qq.com> Date: Fri, 7 Dec 2018 20:24:40 +0800 Subject: [PATCH 4/8] fixed spell error --- contest/tasks.py | 4 ++-- contest/urls/admin.py | 4 ++-- contest/urls/oj.py | 4 ++-- contest/views/admin.py | 6 +++--- contest/views/oj.py | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contest/tasks.py b/contest/tasks.py index 880780014..e424665ab 100644 --- a/contest/tasks.py +++ b/contest/tasks.py @@ -10,13 +10,13 @@ @shared_task(time_limit=30) -def similiar_task(contest_id): +def similar_task(contest_id): problems = Problem.objects.filter(contest_id=contest_id) owner = {} data_to_write = [] for problem in problems: - check_dir = os.path.join(settings.TEST_CASE_DIR, str(problem.id) + "_similiar_tmp") + check_dir = os.path.join(settings.TEST_CASE_DIR, str(problem.id) + "_similar_tmp") if not os.path.exists(check_dir): os.mkdir(check_dir) os.chmod(check_dir, 0o710) diff --git a/contest/urls/admin.py b/contest/urls/admin.py index f82e697bf..af0e6d3c6 100644 --- a/contest/urls/admin.py +++ b/contest/urls/admin.py @@ -1,11 +1,11 @@ from django.conf.urls import url -from ..views.admin import ContestAnnouncementAPI, ContestAPI, ACMContestHelper, DownloadContestSubmissions, ContestCheckSimiliarAPI +from ..views.admin import ContestAnnouncementAPI, ContestAPI, ACMContestHelper, DownloadContestSubmissions, ContestCheckSimilarAPI urlpatterns = [ url(r"^contest/?$", ContestAPI.as_view(), name="contest_admin_api"), url(r"^contest/announcement/?$", ContestAnnouncementAPI.as_view(), name="contest_announcement_admin_api"), url(r"^contest/acm_helper/?$", ACMContestHelper.as_view(), name="acm_contest_helper"), url(r"^download_submissions/?$", DownloadContestSubmissions.as_view(), name="acm_contest_helper"), - url(r"^contesteck_similiar/?$", ContestCheckSimiliarAPI.as_view(), name="contest_check_similiar_admin_api") + url(r"^contest_check_similar/?$", ContestCheckSimilarAPI.as_view(), name="contest_check_similar_admin_api") ] diff --git a/contest/urls/oj.py b/contest/urls/oj.py index c0e09afb8..d7d8fb4cf 100644 --- a/contest/urls/oj.py +++ b/contest/urls/oj.py @@ -4,7 +4,7 @@ from ..views.oj import ContestPasswordVerifyAPI, ContestAccessAPI from ..views.oj import ContestListAPI, ContestAPI from ..views.oj import ContestRankAPI -from ..views.oj import ContestGetSimiliarAPI +from ..views.oj import ContestGetSimilarAPI urlpatterns = [ url(r"^contests/?$", ContestListAPI.as_view(), name="contest_list_api"), @@ -13,5 +13,5 @@ url(r"^contest/announcement/?$", ContestAnnouncementListAPI.as_view(), name="contest_announcement_api"), url(r"^contest/access/?$", ContestAccessAPI.as_view(), name="contest_access_api"), url(r"^contest_rank/?$", ContestRankAPI.as_view(), name="contest_rank_api"), - url(r"^contest/get_similiar/?$", ContestGetSimiliarAPI.as_view(), name="contest_get_similiar_api"), + url(r"^contest/get_similar/?$", ContestGetSimilarAPI.as_view(), name="contest_get_similar_api"), ] diff --git a/contest/views/admin.py b/contest/views/admin.py index 232b3e608..9623840c1 100644 --- a/contest/views/admin.py +++ b/contest/views/admin.py @@ -20,7 +20,7 @@ EditConetestSeriaizer, EditContestAnnouncementSerializer, ACMContesHelperSerializer, ) from account.decorators import super_admin_required -from contest.tasks import similiar_task +from contest.tasks import similar_task class ContestAPI(APIView): @@ -243,9 +243,9 @@ def get(self, request): return resp -class ContestCheckSimiliarAPI(APIView): +class ContestCheckSimilarAPI(APIView): @super_admin_required def get(self, request): cid = request.GET.get("contest_id") - similiar_task.delay(cid) + similar_task.delay(cid) return self.success() diff --git a/contest/views/oj.py b/contest/views/oj.py index 77827cc1b..376c6867b 100644 --- a/contest/views/oj.py +++ b/contest/views/oj.py @@ -187,6 +187,6 @@ def get(self, request): return self.success(page_qs) -class ContestGetSimiliarAPI(APIView): +class ContestGetSimilarAPI(APIView): def get(self, request): return self.success() From 19cee5d10d326226a4566b3a3569318e9a2b895f Mon Sep 17 00:00:00 2001 From: HandsomeHow <673712786@qq.com> Date: Mon, 10 Dec 2018 16:29:14 +0800 Subject: [PATCH 5/8] finish interface for get similar check result --- contest/serializers.py | 6 ++++++ contest/tests.py | 8 ++++++++ contest/views/oj.py | 5 ++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/contest/serializers.py b/contest/serializers.py index 356cddaee..c9f289501 100644 --- a/contest/serializers.py +++ b/contest/serializers.py @@ -106,3 +106,9 @@ class ACMContesHelperSerializer(serializers.Serializer): problem_id = serializers.CharField() rank_id = serializers.IntegerField() checked = serializers.BooleanField() + + +class ContestSimilarResultSerializer(serializers.ModelSerializer): + class Meta: + model = Contest + fields = ["similarity_check_result"] diff --git a/contest/tests.py b/contest/tests.py index 8a21977d6..9c0628fe7 100644 --- a/contest/tests.py +++ b/contest/tests.py @@ -63,6 +63,8 @@ class ContestAPITest(APITestCase): def setUp(self): user = self.create_admin() self.contest = Contest.objects.create(created_by=user, **DEFAULT_CONTEST_DATA) + self.contest.similarity_check_result = ["aaa", "bbb", "ccc"] + self.contest.save() self.url = self.reverse("contest_api") + "?id=" + str(self.contest.id) def test_get_contest_list(self): @@ -97,6 +99,12 @@ def test_regular_user_access_contest(self): resp = self.client.get(self.url) self.assertSuccess(resp) + def test_regular_user_get_similiar(self): + self.create_user("test", "test123") + url = self.reverse("contest_get_similar_api") + resp = self.client.get(url + "?contest_id=" + str(self.contest.id)) + self.assertEqual(resp.data["data"]["similarity_check_result"], ["aaa", "bbb", "ccc"]) + class ContestAnnouncementAdminAPITest(APITestCase): def setUp(self): diff --git a/contest/views/oj.py b/contest/views/oj.py index 376c6867b..ce35ea312 100644 --- a/contest/views/oj.py +++ b/contest/views/oj.py @@ -17,6 +17,7 @@ from ..serializers import ContestAnnouncementSerializer from ..serializers import ContestSerializer, ContestPasswordVerifySerializer from ..serializers import OIContestRankSerializer, ACMContestRankSerializer +from ..serializers import ContestSimilarResultSerializer class ContestAnnouncementListAPI(APIView): @@ -189,4 +190,6 @@ def get(self, request): class ContestGetSimilarAPI(APIView): def get(self, request): - return self.success() + contest_id = request.GET.get("contest_id") + contest = Contest.objects.get(id=contest_id, visible=True) + return self.success(ContestSimilarResultSerializer(contest).data) From 2e463b9c0b984d0ff0e1fd30b6e3adb8c21e04e3 Mon Sep 17 00:00:00 2001 From: HandsomeHow <673712786@qq.com> Date: Thu, 13 Dec 2018 14:20:37 +0800 Subject: [PATCH 6/8] modify similar result format --- contest/tasks.py | 10 +++++++++- contest/tests.py | 5 +++-- judge/dispatcher.py | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/contest/tasks.py b/contest/tasks.py index e424665ab..11d138f01 100644 --- a/contest/tasks.py +++ b/contest/tasks.py @@ -42,7 +42,15 @@ def similar_task(contest_id): if owner[sub1] == owner[sub2]: continue sim = splited_line[3] - data_to_write.append("#".join([problem._id, sub1, owner[sub1], sub2, owner[sub2], sim])) + to_append = { + "problem_id": problem._id, + "submission_a": sub1, + "user_a": owner[sub1], + "submission_b": sub2, + "user_b": owner[sub2], + "similarity": sim + } + data_to_write.append(to_append) f.close() os.remove(output_dir) diff --git a/contest/tests.py b/contest/tests.py index 9c0628fe7..6c6f9ae4c 100644 --- a/contest/tests.py +++ b/contest/tests.py @@ -63,7 +63,7 @@ class ContestAPITest(APITestCase): def setUp(self): user = self.create_admin() self.contest = Contest.objects.create(created_by=user, **DEFAULT_CONTEST_DATA) - self.contest.similarity_check_result = ["aaa", "bbb", "ccc"] + self.contest.similarity_check_result = [{"user1": "aaa", "user2": "bbb"}, {"user1": "ccc", "user2": "ddd"}] self.contest.save() self.url = self.reverse("contest_api") + "?id=" + str(self.contest.id) @@ -103,7 +103,8 @@ def test_regular_user_get_similiar(self): self.create_user("test", "test123") url = self.reverse("contest_get_similar_api") resp = self.client.get(url + "?contest_id=" + str(self.contest.id)) - self.assertEqual(resp.data["data"]["similarity_check_result"], ["aaa", "bbb", "ccc"]) + self.assertEqual(resp.data["data"]["similarity_check_result"][0], {"user1": "aaa", "user2": "bbb"}) + self.assertEqual(resp.data["data"]["similarity_check_result"][1], {"user1": "ccc", "user2": "ddd"}) class ContestAnnouncementAdminAPITest(APITestCase): diff --git a/judge/dispatcher.py b/judge/dispatcher.py index c4480956c..5be08531e 100644 --- a/judge/dispatcher.py +++ b/judge/dispatcher.py @@ -9,7 +9,7 @@ from account.models import User from conf.models import JudgeServer -from contest.models import ContestRuleType, ACMContestRank, OIContestRank, ContestStatus +from contest.models import ContestRuleType, ACMContestRank, OIContestRank from judge.languages import languages, spj_languages from options.options import SysOptions from problem.models import Problem, ProblemRuleType From e74329c6d4de4f3d485be1d674cb95511363a420 Mon Sep 17 00:00:00 2001 From: HandsomeHow <673712786@qq.com> Date: Thu, 13 Dec 2018 19:41:04 +0800 Subject: [PATCH 7/8] make check similar not shared task --- contest/tasks.py | 65 ----------------------------------------- contest/views/admin.py | 66 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 67 deletions(-) delete mode 100644 contest/tasks.py diff --git a/contest/tasks.py b/contest/tasks.py deleted file mode 100644 index 11d138f01..000000000 --- a/contest/tasks.py +++ /dev/null @@ -1,65 +0,0 @@ -from celery import shared_task - -from submission.models import Submission - -import os -from django.conf import settings -from contest.models import Contest -from problem.models import Problem -from submission.models import JudgeStatus - - -@shared_task(time_limit=30) -def similar_task(contest_id): - problems = Problem.objects.filter(contest_id=contest_id) - owner = {} - data_to_write = [] - - for problem in problems: - check_dir = os.path.join(settings.TEST_CASE_DIR, str(problem.id) + "_similar_tmp") - if not os.path.exists(check_dir): - os.mkdir(check_dir) - os.chmod(check_dir, 0o710) - - submissions = Submission.objects.filter(problem_id=problem.id, result=JudgeStatus.ACCEPTED) - for submission in submissions: - owner[submission.id] = submission.username - file_path = os.path.join(check_dir, submission.id) - f = open(file_path, "w") - f.write(submission.code) - f.close() - - output_dir = os.path.join(check_dir, "result") - check_files = check_dir + "/*" - os.system(f"/app/sim_c -p -t40 -o {output_dir} {check_files}") - f = open(output_dir) - for line in f: - # xxxx consists for xx % of xxxx material - if "consists" in line: - splited_line = line.split() - sub1 = splited_line[0].split("/")[-1] - sub2 = splited_line[-2].split("/")[-1] - if owner[sub1] == owner[sub2]: - continue - sim = splited_line[3] - to_append = { - "problem_id": problem._id, - "submission_a": sub1, - "user_a": owner[sub1], - "submission_b": sub2, - "user_b": owner[sub2], - "similarity": sim - } - data_to_write.append(to_append) - f.close() - - os.remove(output_dir) - for submission in submissions: - owner[submission.id] = submission.username - file_path = os.path.join(check_dir, submission.id) - os.remove(file_path) - os.rmdir(check_dir) - - contest = Contest.objects.get(id=contest_id) - contest.similarity_check_result = data_to_write - contest.save() diff --git a/contest/views/admin.py b/contest/views/admin.py index 9623840c1..4f5979d1c 100644 --- a/contest/views/admin.py +++ b/contest/views/admin.py @@ -5,9 +5,12 @@ import dateutil.parser from django.http import FileResponse +from django.conf import settings from account.decorators import check_contest_permission, ensure_created_by from account.models import User +from contest.models import ContestStatus +from problem.models import Problem from submission.models import Submission, JudgeStatus from utils.api import APIView, validate_serializer from utils.cache import cache @@ -20,7 +23,6 @@ EditConetestSeriaizer, EditContestAnnouncementSerializer, ACMContesHelperSerializer, ) from account.decorators import super_admin_required -from contest.tasks import similar_task class ContestAPI(APIView): @@ -247,5 +249,65 @@ class ContestCheckSimilarAPI(APIView): @super_admin_required def get(self, request): cid = request.GET.get("contest_id") - similar_task.delay(cid) + contest = Contest.objects.get(id=cid) + if contest.status != ContestStatus.CONTEST_ENDED: + return self.error("Contest not ended") + problems = Problem.objects.filter(contest_id=cid) + owner = {} + data_to_write = [] + + for problem in problems: + check_dir = os.path.join(settings.TEST_CASE_DIR, str(problem.id) + "_similar_tmp") + if not os.path.exists(check_dir): + os.mkdir(check_dir) + os.chmod(check_dir, 0o710) + + submissions = Submission.objects.filter(problem_id=problem.id, result=JudgeStatus.ACCEPTED) + for submission in submissions: + owner[submission.id] = submission.username + file_path = os.path.join(check_dir, submission.id) + f = open(file_path, "w") + f.write(submission.code) + f.close() + + output_dir = os.path.join(check_dir, "result") + check_files = check_dir + "/*" + os.system(f"/app/sim_c -p -t40 -o {output_dir} {check_files}") + f = open(output_dir) + similar_submissions = [] + for line in f: + # xxxx consists for xx % of xxxx material + if "consists" in line: + splited_line = line.split() + sub1 = splited_line[0].split("/")[-1] + sub2 = splited_line[-2].split("/")[-1] + if owner[sub1] == owner[sub2]: + continue + similar_submissions.append(sub1) + similar_submissions.append(sub2) + sim = splited_line[3] + to_append = { + "problem_id": problem._id, + "submission_a": sub1, + "user_a": owner[sub1], + "submission_b": sub2, + "user_b": owner[sub2], + "similarity": sim + } + data_to_write.append(to_append) + f.close() + + os.remove(output_dir) + for submission in submissions: + owner[submission.id] = submission.username + file_path = os.path.join(check_dir, submission.id) + os.remove(file_path) + if submission.id in similar_submissions: + submission.shared = True + submission.save() + os.rmdir(check_dir) + + contest.similarity_check_result = data_to_write + contest.save() + return self.success() From 931b4cabf5e56944b1764771302825da1e5cea12 Mon Sep 17 00:00:00 2001 From: HandsomeHow <673712786@qq.com> Date: Wed, 19 Dec 2018 11:23:28 +0800 Subject: [PATCH 8/8] bug fixed --- judge/dispatcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/judge/dispatcher.py b/judge/dispatcher.py index 5be08531e..440de151c 100644 --- a/judge/dispatcher.py +++ b/judge/dispatcher.py @@ -177,7 +177,7 @@ def judge(self): self.release_judge_server(server.id) if self.contest_id: - if self.contest.status != ContestStatus.CONTEST_UNDERWAY or \ + if self.submission.create_time > self.contest.end_time or \ User.objects.get(id=self.submission.user_id).is_contest_admin(self.contest): logger.info( "Contest debug mode, id: " + str(self.contest_id) + ", submission id: " + self.submission.id)