ranklist相关的改动

This commit is contained in:
zemal 2017-08-20 20:32:07 +08:00
parent 3b1f02c356
commit 07643e2639
6 changed files with 97 additions and 24 deletions

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.6 on 2017-08-20 02:03
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0002_auto_20170209_1028'),
]
operations = [
migrations.AddField(
model_name='userprofile',
name='total_score',
field=models.BigIntegerField(default=0),
),
migrations.RenameField(
model_name='userprofile',
old_name='accepted_problem_number',
new_name='accepted_number',
),
]

View File

@ -69,28 +69,38 @@ def _random_avatar():
class UserProfile(models.Model):
user = models.OneToOneField(User)
# Store user problem solution status with json string format
# {"problems": {1: JudgeStatus.ACCEPTED}, "contest_problems": {20: JudgeStatus.PENDING)}
# Store user problem solution status with json string format, Only for problems not contest_problems
# ACM: {1: {status: JudgeStatus.ACCEPTED}}
# OI: {1: {score: 33}}
problems_status = JSONField(default={})
avatar = models.CharField(max_length=50, default=_random_avatar)
blog = models.URLField(blank=True, null=True)
mood = models.CharField(max_length=200, blank=True, null=True)
accepted_problem_number = models.IntegerField(default=0)
submission_number = models.IntegerField(default=0)
phone_number = models.CharField(max_length=15, blank=True, null=True)
school = models.CharField(max_length=200, blank=True, null=True)
major = models.CharField(max_length=200, blank=True, null=True)
student_id = models.CharField(max_length=15, blank=True, null=True)
time_zone = models.CharField(max_length=32, blank=True, null=True)
language = models.CharField(max_length=32, blank=True, null=True)
# for ACM
accepted_number = models.IntegerField(default=0)
# for OI
total_score = models.BigIntegerField(default=0)
submission_number = models.IntegerField(default=0)
def add_accepted_problem_number(self):
self.accepted_problem_number = models.F("accepted_problem_number") + 1
self.accepted_number = models.F("accepted_number") + 1
self.save()
def add_submission_number(self):
self.submission_number = models.F("submission_number") + 1
self.save()
# 计算总分时, 应先减掉上次该题所得分数, 然后再加上本次所得分数
def add_score(self, this_time_score, last_time_score=None):
last_time_score = last_time_score or 0
self.total_score = models.F("total_score") - last_time_score + this_time_score
self.save()
class Meta:
db_table = "user_profile"

View File

@ -1,6 +1,6 @@
from django import forms
from utils.api import DateTimeTZField, serializers
from utils.api import DateTimeTZField, serializers, UsernameSerializer
from .models import AdminType, ProblemPermission, User, UserProfile
@ -97,3 +97,10 @@ class TwoFactorAuthCodeSerializer(serializers.Serializer):
class AvatarUploadForm(forms.Form):
file = forms.FileField()
class RankInfoSerializer(serializers.ModelSerializer):
user = UsernameSerializer()
class Meta:
model = UserProfile

View File

@ -3,7 +3,8 @@ from django.conf.urls import url
from ..views.oj import (ApplyResetPasswordAPI, ResetPasswordAPI,
UserChangePasswordAPI, UserRegisterAPI,
UserLoginAPI, UserLogoutAPI, UsernameOrEmailCheck,
SSOAPI, AvatarUploadAPI, TwoFactorAuthAPI, UserProfileAPI)
SSOAPI, AvatarUploadAPI, TwoFactorAuthAPI, UserProfileAPI,
UserRankAPI)
from utils.captcha.views import CaptchaAPIView
@ -19,5 +20,6 @@ urlpatterns = [
url(r"^profile/?$", UserProfileAPI.as_view(), name="user_profile_api"),
url(r"^avatar/upload/?$", AvatarUploadAPI.as_view(), name="avatar_upload_api"),
url(r"^sso/?$", SSOAPI.as_view(), name="sso_api"),
url(r"^two_factor_auth/?$", TwoFactorAuthAPI.as_view(), name="two_factor_auth_api")
url(r"^two_factor_auth/?$", TwoFactorAuthAPI.as_view(), name="two_factor_auth_api"),
url(r"^user_rank/?$", UserRankAPI.as_view(), name="user_rank_api"),
]

View File

@ -18,10 +18,10 @@ from utils.shortcuts import rand_str
from ..decorators import login_required
from ..models import User, UserProfile
from ..serializers import (ApplyResetPasswordSerializer,
ResetPasswordSerializer,
from ..serializers import (ApplyResetPasswordSerializer, ResetPasswordSerializer,
UserChangePasswordSerializer, UserLoginSerializer,
UserRegisterSerializer, UsernameOrEmailCheckSerializer)
UserRegisterSerializer, UsernameOrEmailCheckSerializer,
RankInfoSerializer)
from ..serializers import (SSOSerializer, TwoFactorAuthCodeSerializer,
UserProfileSerializer,
EditUserProfileSerializer, AvatarUploadForm)
@ -32,6 +32,7 @@ class UserProfileAPI(APIView):
"""
判断是否登录 若登录返回用户信息
"""
@method_decorator(ensure_csrf_cookie)
def get(self, request, **kwargs):
user = request.user
@ -321,3 +322,16 @@ class ResetPasswordAPI(APIView):
user.set_password(data["password"])
user.save()
return self.success("Succeeded")
class UserRankAPI(APIView):
def get(self, request):
rule_type = request.GET.get("rule")
if rule_type not in ["acm", "oi"]:
rule_type = "acm"
profiles = UserProfile.objects.select_related("user").filter(submission_number__gt=0)
if rule_type == "acm":
profiles = profiles.order_by("-accepted_number", "submission_number")
else:
profiles = profiles.order_by("-total_score")
return self.success(self.paginate_data(request, profiles, RankInfoSerializer))

View File

@ -40,7 +40,7 @@ class JudgeDispatcher(object):
.get(_id=problem_id, contest_id=self.submission.contest_id)
self.contest = self.problem.contest
else:
self.problem = Problem.objects.get(pk=problem_id)
self.problem = Problem.objects.get(_id=problem_id)
def _request(self, url, data=None):
kwargs = {"headers": {"X-Judge-Server-Token": self.token,
@ -75,7 +75,7 @@ class JudgeDispatcher(object):
def judge(self, output=False):
server = self.choose_judge_server()
if not server:
data = {"submission_id": self.submission.id, "problem_id": self.problem.id}
data = {"submission_id": self.submission.id, "problem_id": self.problem._id}
self.redis_conn.lpush(CacheKey.waiting_queue, json.dumps(data))
return
@ -111,6 +111,7 @@ class JudgeDispatcher(object):
# 用时和内存占用保存为多个测试点中最长的那个
self.submission.statistic_info["time_cost"] = max([x["cpu_time"] for x in resp["data"]])
self.submission.statistic_info["memory_cost"] = max([x["memory"] for x in resp["data"]])
# todo OI statistic_info["score"]
error_test_case = list(filter(lambda case: case["result"] != 0, resp["data"]))
# 多个测试点全部正确则AC否则 ACM模式下取第一个错误的测试点的状态, OI模式若全部错误则取第一个错误测试点状态否则为部分正确
@ -144,9 +145,9 @@ class JudgeDispatcher(object):
self.problem.add_ac_number()
with transaction.atomic():
if self.submission.contest_id:
problem = ContestProblem.objects.select_for_update().get(_id=self.problem.id, contest_id=self.contest.id)
problem = ContestProblem.objects.select_for_update().get(_id=self.problem._id, contest_id=self.contest.id)
else:
problem = Problem.objects.select_related().get(_id=self.problem.id)
problem = Problem.objects.select_related().get(_id=self.problem._id)
info = problem.statistic_info
result = str(self.submission.result)
info[result] = info.get(result, 0) + 1
@ -155,21 +156,35 @@ class JudgeDispatcher(object):
def update_user_profile(self):
with transaction.atomic():
# 更新user profile
user = User.objects.select_for_update().get(id=self.submission.user_id)
user_profile = user.userprofile
user_profile.add_submission_number()
problems_status = user_profile.problems_status
if "problems" not in problems_status:
problems_status["problems"] = {}
# 之前状态不是ac, 现在是ac了 需要更新用户ac题目数量计数器,这里需要判重
if problems_status["problems"].get(str(self.problem.id), JudgeStatus.WRONG_ANSWER) != JudgeStatus.ACCEPTED:
if self.submission.result == JudgeStatus.ACCEPTED:
user_profile.add_accepted_problem_number()
problems_status["problems"][str(self.problem.id)] = JudgeStatus.ACCEPTED
problem_id = str(self.problem._id)
if self.problem.rule_type == ProblemRuleType.ACM:
if problem_id not in problems_status:
problems_status[problem_id] = {"status": self.submission.result}
if self.submission.result == JudgeStatus.ACCEPTED:
user_profile.add_accepted_problem_number()
# 以前提交过, ac了直接略过
elif problems_status[problem_id]["status"] != JudgeStatus.ACCEPTED:
if self.submission.result == JudgeStatus.ACCEPTED:
user_profile.add_accepted_problem_number()
problems_status[problem_id]["status"] = JudgeStatus.ACCEPTED
else:
problems_status[problem_id]["status"] = self.submission.result
else:
score = self.submission.statistic_info["score"]
if problem_id not in problems_status:
user_profile.add_score(score)
problems_status[problem_id] = {"score": score}
else:
problems_status["problems"][str(self.problem.id)] = JudgeStatus.WRONG_ANSWER
# 加上本次 减掉上次的score
user_profile.add_score(score, problems_status[problem_id]["score"])
problems_status[problem_id] = {"score": score}
user_profile.problems_status = problems_status
user_profile.save(update_fields=["problems_status"])