add pick one api

This commit is contained in:
zema1 2017-10-21 20:39:39 +08:00
parent c1d099ed45
commit 5d03ec5aab
13 changed files with 96 additions and 55 deletions

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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,49 +152,35 @@ 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:
logger.info("Contest debug mode, id: " + str(self.contest_id) + ", submission id: " + self.submission.id)
return
with transaction.atomic():
# prepare problem and user_profile
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) result = str(self.submission.result)
problem_info[result] = problem_info.get(result, 0) + 1 problem_id = str(self.problem.id)
problem.statistic_info = problem_info with transaction.atomic():
# update problem status
# update submission and accepted number counter problem = Problem.objects.select_for_update().get(contest_id=self.contest_id, id=self.problem.id)
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)
@ -201,14 +189,54 @@ class JudgeDispatcher(object):
"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:

View File

@ -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',)},
),
] ]

View File

@ -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

View File

@ -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

View File

@ -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"),
] ]

View File

@ -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):
@ -95,7 +105,6 @@ class ContestProblemAPI(APIView):
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

View File

@ -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

View File

@ -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)