mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2024-09-21 00:13:18 +00:00
add pick one api
This commit is contained in:
parent
c1d099ed45
commit
5d03ec5aab
@ -10,7 +10,7 @@ class SessionRecordMiddleware(MiddlewareMixin):
|
|||||||
if request.user.is_authenticated():
|
if request.user.is_authenticated():
|
||||||
session = request.session
|
session = request.session
|
||||||
session["user_agent"] = request.META.get("HTTP_USER_AGENT", "")
|
session["user_agent"] = request.META.get("HTTP_USER_AGENT", "")
|
||||||
session["ip"] = request.META.get("HTTP_X_REAL_IP", "UNKNOWN IP")
|
session["ip"] = request.META.get("HTTP_X_REAL_IP", request.META.get("REMOTE_ADDR"))
|
||||||
session["last_activity"] = now()
|
session["last_activity"] = now()
|
||||||
user_sessions = request.user.session_keys
|
user_sessions = request.user.session_keys
|
||||||
if session.session_key not in user_sessions:
|
if session.session_key not in user_sessions:
|
||||||
|
@ -4,7 +4,6 @@ from django.utils import timezone
|
|||||||
|
|
||||||
from options.options import SysOptions
|
from options.options import SysOptions
|
||||||
from utils.api.tests import APITestCase
|
from utils.api.tests import APITestCase
|
||||||
from utils.constants import CacheKey
|
|
||||||
from .models import JudgeServer
|
from .models import JudgeServer
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ class Contest(models.Model):
|
|||||||
return user.is_authenticated() and (self.created_by == user or user.admin_type == AdminType.SUPER_ADMIN)
|
return user.is_authenticated() and (self.created_by == user or user.admin_type == AdminType.SUPER_ADMIN)
|
||||||
|
|
||||||
def check_oi_permission(self, user):
|
def check_oi_permission(self, user):
|
||||||
if self.status != ContestStatus.CONTEST_ENDED and self.real_time_rank == False:
|
if self.status != ContestStatus.CONTEST_ENDED and not self.real_time_rank:
|
||||||
if self.is_contest_admin(user):
|
if self.is_contest_admin(user):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM python:3.5
|
FROM python:3.6
|
||||||
ADD requirements.txt /tmp
|
ADD requirements.txt /tmp
|
||||||
RUN pip install -r /tmp/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
|
RUN pip install -r /tmp/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
django==1.11.4
|
django==1.11.4
|
||||||
djangorestframework==3.4.0
|
djangorestframework==3.4.0
|
||||||
pillow
|
pillow
|
||||||
jsonfield
|
|
||||||
otpauth
|
otpauth
|
||||||
flake8-quotes
|
flake8-quotes
|
||||||
pytz
|
pytz
|
||||||
|
@ -136,9 +136,11 @@ class JudgeDispatcher(object):
|
|||||||
self.submission.save()
|
self.submission.save()
|
||||||
self.release_judge_server(server.id)
|
self.release_judge_server(server.id)
|
||||||
|
|
||||||
self.update_problem_status()
|
|
||||||
if self.contest_id:
|
if self.contest_id:
|
||||||
|
self.update_contest_problem_status()
|
||||||
self.update_contest_rank()
|
self.update_contest_rank()
|
||||||
|
else:
|
||||||
|
self.update_problem_status()
|
||||||
|
|
||||||
# 至此判题结束,尝试处理任务队列中剩余的任务
|
# 至此判题结束,尝试处理任务队列中剩余的任务
|
||||||
process_pending_task()
|
process_pending_task()
|
||||||
@ -150,65 +152,91 @@ class JudgeDispatcher(object):
|
|||||||
return self._request(urljoin(service_url, "compile_spj"), data=data)
|
return self._request(urljoin(service_url, "compile_spj"), data=data)
|
||||||
|
|
||||||
def update_problem_status(self):
|
def update_problem_status(self):
|
||||||
if self.contest_id and self.contest.status != ContestStatus.CONTEST_UNDERWAY:
|
result = str(self.submission.result)
|
||||||
logger.info("Contest debug mode, id: " + str(self.contest_id) + ", submission id: " + self.submission.id)
|
problem_id = str(self.problem.id)
|
||||||
return
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
# prepare problem and user_profile
|
# update problem status
|
||||||
problem = Problem.objects.select_for_update().get(contest_id=self.contest_id, id=self.problem.id)
|
problem = Problem.objects.select_for_update().get(contest_id=self.contest_id, id=self.problem.id)
|
||||||
user = User.objects.select_for_update().select_for_update("userprofile").get(id=self.submission.user_id)
|
|
||||||
user_profile = user.userprofile
|
|
||||||
if self.contest_id:
|
|
||||||
key = "contest_problems"
|
|
||||||
else:
|
|
||||||
key = "problems"
|
|
||||||
acm_problems_status = user_profile.acm_problems_status.get(key, {})
|
|
||||||
oi_problems_status = user_profile.oi_problems_status.get(key, {})
|
|
||||||
problem_id = str(self.problem.id)
|
|
||||||
problem_info = problem.statistic_info
|
|
||||||
|
|
||||||
# update problem info
|
|
||||||
result = str(self.submission.result)
|
|
||||||
problem_info[result] = problem_info.get(result, 0) + 1
|
|
||||||
problem.statistic_info = problem_info
|
|
||||||
|
|
||||||
# update submission and accepted number counter
|
|
||||||
problem.submission_number += 1
|
problem.submission_number += 1
|
||||||
if self.submission.result == JudgeStatus.ACCEPTED:
|
if self.submission.result == JudgeStatus.ACCEPTED:
|
||||||
problem.accepted_number += 1
|
problem.accepted_number += 1
|
||||||
# submission in a contest will not be counted in user profile
|
problem_info = problem.statistic_info
|
||||||
if not self.contest_id:
|
problem_info[result] = problem_info.get(result, 0) + 1
|
||||||
|
problem.save(update_fields=["accepted_number", "submission_number", "statistic_info"])
|
||||||
|
|
||||||
|
# update_userprofile
|
||||||
|
user = User.objects.select_for_update().get(id=self.submission.user_id)
|
||||||
|
user_profile = user.userprofile
|
||||||
|
if problem.rule_type == ProblemRuleType.ACM:
|
||||||
user_profile.submission_number += 1
|
user_profile.submission_number += 1
|
||||||
if self.submission.result == JudgeStatus.ACCEPTED:
|
if self.submission.result == JudgeStatus.ACCEPTED:
|
||||||
user_profile.accepted_number += 1
|
user_profile.accepted_number += 1
|
||||||
|
acm_problems_status = user_profile.acm_problems_status.get("problems", {})
|
||||||
if self.problem.rule_type == ProblemRuleType.ACM:
|
|
||||||
# update user_profile
|
|
||||||
if problem_id not in acm_problems_status:
|
if problem_id not in acm_problems_status:
|
||||||
acm_problems_status[problem_id] = {"status": self.submission.result, "_id": self.problem._id}
|
acm_problems_status[problem_id] = {"status": self.submission.result, "_id": self.problem._id}
|
||||||
# skip if the problem has been accepted
|
|
||||||
elif acm_problems_status[problem_id]["status"] != JudgeStatus.ACCEPTED:
|
elif acm_problems_status[problem_id]["status"] != JudgeStatus.ACCEPTED:
|
||||||
acm_problems_status[problem_id]["status"] = self.submission.result
|
acm_problems_status[problem_id]["status"] = self.submission.result
|
||||||
user_profile.acm_problems_status[key] = acm_problems_status
|
user_profile.acm_problems_status["problems"] = acm_problems_status
|
||||||
|
user_profile.save(update_fields=["submission_number", "accepted_number", "acm_problems_status"])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# update user_profile
|
oi_problems_status = user_profile.oi_problems_status.get("problems", {})
|
||||||
score = self.submission.statistic_info["score"]
|
score = self.submission.statistic_info["score"]
|
||||||
if problem_id not in oi_problems_status:
|
if problem_id not in oi_problems_status:
|
||||||
user_profile.add_score(score)
|
user_profile.add_score(score)
|
||||||
oi_problems_status[problem_id] = {"status": self.submission.result,
|
oi_problems_status[problem_id] = {"status": self.submission.result,
|
||||||
"_id": self.problem._id,
|
"_id": self.problem._id,
|
||||||
"score": score}
|
"score": score}
|
||||||
else:
|
else:
|
||||||
# minus last time score, add this time score
|
# minus last time score, add this time score
|
||||||
user_profile.add_score(this_time_score=score, last_time_score=oi_problems_status[problem_id]["score"])
|
user_profile.add_score(this_time_score=score,
|
||||||
|
last_time_score=oi_problems_status[problem_id]["score"])
|
||||||
oi_problems_status[problem_id]["score"] = score
|
oi_problems_status[problem_id]["score"] = score
|
||||||
oi_problems_status[problem_id]["status"] = self.submission.result
|
oi_problems_status[problem_id]["status"] = self.submission.result
|
||||||
user_profile.oi_problems_status[key] = oi_problems_status
|
user_profile.oi_problems_status["problems"] = oi_problems_status
|
||||||
|
user_profile.save(update_fields=["oi_problems_status"])
|
||||||
|
|
||||||
|
def update_contest_problem_status(self):
|
||||||
|
if self.contest_id and self.contest.status != ContestStatus.CONTEST_UNDERWAY:
|
||||||
|
logger.info("Contest debug mode, id: " + str(self.contest_id) + ", submission id: " + self.submission.id)
|
||||||
|
return
|
||||||
|
with transaction.atomic():
|
||||||
|
user = User.objects.select_for_update().select_related("userprofile").get(id=self.submission.user_id)
|
||||||
|
user_profile = user.userprofile
|
||||||
|
problem_id = str(self.problem.id)
|
||||||
|
if self.contest.rule_type == ContestRuleType.ACM:
|
||||||
|
contest_problems_status = user_profile.acm_problems_status.get("contest_problems", {})
|
||||||
|
if problem_id not in contest_problems_status:
|
||||||
|
contest_problems_status[problem_id] = {"status": self.submission.result, "_id": self.problem._id}
|
||||||
|
elif contest_problems_status[problem_id]["status"] != JudgeStatus.ACCEPTED:
|
||||||
|
contest_problems_status[problem_id]["status"] = self.submission.result
|
||||||
|
else:
|
||||||
|
# 如果已AC, 直接跳过 不计入任何计数器
|
||||||
|
return
|
||||||
|
user_profile.acm_problems_status["contest_problems"] = contest_problems_status
|
||||||
|
user_profile.save(update_fields=["acm_problems_status"])
|
||||||
|
|
||||||
|
elif self.contest.rule_type == ContestRuleType.OI:
|
||||||
|
contest_problems_status = user_profile.oi_problems_status.get("contest_problems", {})
|
||||||
|
score = self.submission.statistic_info["score"]
|
||||||
|
if problem_id not in contest_problems_status:
|
||||||
|
contest_problems_status[problem_id] = {"status": self.submission.result,
|
||||||
|
"_id": self.problem._id,
|
||||||
|
"score": score}
|
||||||
|
else:
|
||||||
|
contest_problems_status[problem_id]["score"] = score
|
||||||
|
contest_problems_status[problem_id]["status"] = self.submission.result
|
||||||
|
user_profile.oi_problems_status["contest_problems"] = contest_problems_status
|
||||||
|
user_profile.save(update_fields=["oi_problems_status"])
|
||||||
|
|
||||||
|
problem = Problem.objects.select_for_update().get(contest_id=self.contest_id, id=self.problem.id)
|
||||||
|
result = str(self.submission.result)
|
||||||
|
problem_info = problem.statistic_info
|
||||||
|
problem_info[result] = problem_info.get(result, 0) + 1
|
||||||
|
problem.submission_number += 1
|
||||||
|
if self.submission.result == JudgeStatus.ACCEPTED:
|
||||||
|
problem.accepted_number += 1
|
||||||
problem.save(update_fields=["submission_number", "accepted_number", "statistic_info"])
|
problem.save(update_fields=["submission_number", "accepted_number", "statistic_info"])
|
||||||
user_profile.save(update_fields=[
|
|
||||||
"submission_number", "accepted_number", "acm_problems_status", "oi_problems_status"])
|
|
||||||
|
|
||||||
def update_contest_rank(self):
|
def update_contest_rank(self):
|
||||||
if self.contest_id and self.contest.status != ContestStatus.CONTEST_UNDERWAY:
|
if self.contest_id and self.contest.status != ContestStatus.CONTEST_UNDERWAY:
|
||||||
|
@ -38,4 +38,8 @@ class Migration(migrations.Migration):
|
|||||||
name='test_case_score',
|
name='test_case_score',
|
||||||
field=django.contrib.postgres.fields.jsonb.JSONField(),
|
field=django.contrib.postgres.fields.jsonb.JSONField(),
|
||||||
),
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='problem',
|
||||||
|
options={'ordering': ('create_time',)},
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -71,6 +71,7 @@ class Problem(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
db_table = "problem"
|
db_table = "problem"
|
||||||
unique_together = (("_id", "contest"),)
|
unique_together = (("_id", "contest"),)
|
||||||
|
ordering = ("create_time",)
|
||||||
|
|
||||||
def add_submission_number(self):
|
def add_submission_number(self):
|
||||||
self.submission_number = models.F("submission_number") + 1
|
self.submission_number = models.F("submission_number") + 1
|
||||||
|
@ -25,6 +25,7 @@ DEFAULT_PROBLEM_DATA = {"_id": "A-110", "title": "test", "description": "<p>test
|
|||||||
"input_size": 0, "score": 0}],
|
"input_size": 0, "score": 0}],
|
||||||
"rule_type": "ACM", "hint": "<p>test</p>", "source": "test"}
|
"rule_type": "ACM", "hint": "<p>test</p>", "source": "test"}
|
||||||
|
|
||||||
|
|
||||||
class ProblemCreateTestBase(APITestCase):
|
class ProblemCreateTestBase(APITestCase):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_problem(problem_data, created_by):
|
def add_problem(problem_data, created_by):
|
||||||
@ -32,7 +33,8 @@ class ProblemCreateTestBase(APITestCase):
|
|||||||
if data["spj"]:
|
if data["spj"]:
|
||||||
if not data["spj_language"] or not data["spj_code"]:
|
if not data["spj_language"] or not data["spj_code"]:
|
||||||
raise ValueError("Invalid spj")
|
raise ValueError("Invalid spj")
|
||||||
data["spj_version"] = hashlib.md5((data["spj_language"] + ":" + data["spj_code"]).encode("utf-8")).hexdigest()
|
data["spj_version"] = hashlib.md5(
|
||||||
|
(data["spj_language"] + ":" + data["spj_code"]).encode("utf-8")).hexdigest()
|
||||||
else:
|
else:
|
||||||
data["spj_language"] = None
|
data["spj_language"] = None
|
||||||
data["spj_code"] = None
|
data["spj_code"] = None
|
||||||
@ -215,7 +217,7 @@ class ContestProblemAdminTest(APITestCase):
|
|||||||
self.assertEqual(len(resp.data["data"]), 1)
|
self.assertEqual(len(resp.data["data"]), 1)
|
||||||
|
|
||||||
def test_get_one_contest_problem(self):
|
def test_get_one_contest_problem(self):
|
||||||
contest, contest_problem = self.test_create_contest_problem()
|
contest, contest_problem = self.test_create_contest_problem()
|
||||||
contest_id = contest.data["data"]["id"]
|
contest_id = contest.data["data"]["id"]
|
||||||
problem_id = contest_problem.data["data"]["id"]
|
problem_id = contest_problem.data["data"]["id"]
|
||||||
resp = self.client.get(f"{self.url}?contest_id={contest_id}&id={problem_id}")
|
resp = self.client.get(f"{self.url}?contest_id={contest_id}&id={problem_id}")
|
||||||
@ -233,7 +235,7 @@ class ContestProblemTest(ProblemCreateTestBase):
|
|||||||
self.problem = self.add_problem(DEFAULT_PROBLEM_DATA, admin)
|
self.problem = self.add_problem(DEFAULT_PROBLEM_DATA, admin)
|
||||||
self.problem.contest_id = self.contest["id"]
|
self.problem.contest_id = self.contest["id"]
|
||||||
self.problem.save()
|
self.problem.save()
|
||||||
self.url = self.reverse("contest_problem_api")
|
self.url = self.reverse("contest_problem_api")
|
||||||
|
|
||||||
def test_admin_get_contest_problem_list(self):
|
def test_admin_get_contest_problem_list(self):
|
||||||
contest_id = self.contest["id"]
|
contest_id = self.contest["id"]
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from ..views.oj import ProblemTagAPI, ProblemAPI, ContestProblemAPI
|
from ..views.oj import ProblemTagAPI, ProblemAPI, ContestProblemAPI, PickOneAPI
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r"^problem/tags/?$", ProblemTagAPI.as_view(), name="problem_tag_list_api"),
|
url(r"^problem/tags/?$", ProblemTagAPI.as_view(), name="problem_tag_list_api"),
|
||||||
url(r"^problem/?$", ProblemAPI.as_view(), name="problem_api"),
|
url(r"^problem/?$", ProblemAPI.as_view(), name="problem_api"),
|
||||||
|
url(r"^pickone/?$", PickOneAPI.as_view(), name="pick_one_api"),
|
||||||
url(r"^contest/problem/?$", ContestProblemAPI.as_view(), name="contest_problem_api"),
|
url(r"^contest/problem/?$", ContestProblemAPI.as_view(), name="contest_problem_api"),
|
||||||
]
|
]
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import random
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from utils.api import APIView
|
from utils.api import APIView
|
||||||
from account.decorators import check_contest_permission
|
from account.decorators import check_contest_permission
|
||||||
@ -12,6 +13,15 @@ class ProblemTagAPI(APIView):
|
|||||||
return self.success(TagSerializer(ProblemTag.objects.all(), many=True).data)
|
return self.success(TagSerializer(ProblemTag.objects.all(), many=True).data)
|
||||||
|
|
||||||
|
|
||||||
|
class PickOneAPI(APIView):
|
||||||
|
def get(self, request):
|
||||||
|
problems = Problem.objects.filter(contest_id__isnull=True, visible=True)
|
||||||
|
count = problems.count()
|
||||||
|
if count == 0:
|
||||||
|
return self.error("No problem to pick")
|
||||||
|
return self.success(problems[random.randint(0, count - 1)]._id)
|
||||||
|
|
||||||
|
|
||||||
class ProblemAPI(APIView):
|
class ProblemAPI(APIView):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _add_problem_status(request, queryset_values):
|
def _add_problem_status(request, queryset_values):
|
||||||
@ -24,7 +34,7 @@ class ProblemAPI(APIView):
|
|||||||
if results is not None:
|
if results is not None:
|
||||||
problems = results
|
problems = results
|
||||||
else:
|
else:
|
||||||
problems = [queryset_values,]
|
problems = [queryset_values, ]
|
||||||
for problem in problems:
|
for problem in problems:
|
||||||
if problem["rule_type"] == ProblemRuleType.ACM:
|
if problem["rule_type"] == ProblemRuleType.ACM:
|
||||||
problem["my_status"] = acm_problems_status.get(str(problem["id"]), {}).get("status")
|
problem["my_status"] = acm_problems_status.get(str(problem["id"]), {}).get("status")
|
||||||
@ -36,7 +46,7 @@ class ProblemAPI(APIView):
|
|||||||
problem_id = request.GET.get("problem_id")
|
problem_id = request.GET.get("problem_id")
|
||||||
if problem_id:
|
if problem_id:
|
||||||
try:
|
try:
|
||||||
problem = Problem.objects.select_related("created_by")\
|
problem = Problem.objects.select_related("created_by") \
|
||||||
.get(_id=problem_id, contest_id__isnull=True, visible=True)
|
.get(_id=problem_id, contest_id__isnull=True, visible=True)
|
||||||
problem_data = ProblemSerializer(problem).data
|
problem_data = ProblemSerializer(problem).data
|
||||||
self._add_problem_status(request, problem_data)
|
self._add_problem_status(request, problem_data)
|
||||||
@ -93,9 +103,8 @@ class ContestProblemAPI(APIView):
|
|||||||
except Problem.DoesNotExist:
|
except Problem.DoesNotExist:
|
||||||
return self.error("Problem does not exist.")
|
return self.error("Problem does not exist.")
|
||||||
problem_data = ContestProblemSerializer(problem).data
|
problem_data = ContestProblemSerializer(problem).data
|
||||||
self._add_problem_status(request, [problem_data,])
|
self._add_problem_status(request, [problem_data, ])
|
||||||
return self.success(problem_data)
|
return self.success(problem_data)
|
||||||
|
|
||||||
contest_problems = Problem.objects.select_related("created_by").filter(contest=self.contest, visible=True)
|
contest_problems = Problem.objects.select_related("created_by").filter(contest=self.contest, visible=True)
|
||||||
# 根据profile, 为做过的题目添加标记
|
# 根据profile, 为做过的题目添加标记
|
||||||
data = ContestProblemSerializer(contest_problems, many=True).data
|
data = ContestProblemSerializer(contest_problems, many=True).data
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from utils.models import JSONField
|
from utils.models import JSONField
|
||||||
from account.models import AdminType
|
|
||||||
from problem.models import Problem
|
from problem.models import Problem
|
||||||
from contest.models import Contest
|
from contest.models import Contest
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from account.decorators import login_required, check_contest_permission
|
from account.decorators import login_required, check_contest_permission
|
||||||
from judge.tasks import judge_task
|
# from judge.tasks import judge_task
|
||||||
# from judge.dispatcher import JudgeDispatcher
|
from judge.dispatcher import JudgeDispatcher
|
||||||
from problem.models import Problem, ProblemRuleType
|
from problem.models import Problem, ProblemRuleType
|
||||||
from contest.models import Contest, ContestStatus, ContestRuleType
|
from contest.models import Contest, ContestStatus, ContestRuleType
|
||||||
from utils.api import APIView, validate_serializer
|
from utils.api import APIView, validate_serializer
|
||||||
@ -39,8 +39,8 @@ def _submit(response, user, problem_id, language, code, contest_id):
|
|||||||
problem_id=problem.id,
|
problem_id=problem.id,
|
||||||
contest_id=contest_id)
|
contest_id=contest_id)
|
||||||
# use this for debug
|
# use this for debug
|
||||||
# JudgeDispatcher(submission.id, problem.id).judge()
|
JudgeDispatcher(submission.id, problem.id).judge()
|
||||||
judge_task.delay(submission.id, problem.id)
|
# judge_task.delay(submission.id, problem.id)
|
||||||
return response.success({"submission_id": submission.id})
|
return response.success({"submission_id": submission.id})
|
||||||
|
|
||||||
|
|
||||||
@ -160,4 +160,3 @@ class ContestSubmissionListAPI(APIView):
|
|||||||
data = self.paginate_data(request, submissions)
|
data = self.paginate_data(request, submissions)
|
||||||
data["results"] = SubmissionListSerializer(data["results"], many=True, user=request.user).data
|
data["results"] = SubmissionListSerializer(data["results"], many=True, user=request.user).data
|
||||||
return self.success(data)
|
return self.success(data)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user