Move real_name to UserProfile;

Delete student_id field;
Mark the problems that have submission;
Alter dispatcher to adapt the changes.
This commit is contained in:
zema1 2017-09-12 11:45:17 +08:00
parent 1e4ede6d1a
commit f55a242ec0
15 changed files with 221 additions and 113 deletions

View File

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-08-30 11:54
from __future__ import unicode_literals
from django.db import migrations, models
import jsonfield.fields
class Migration(migrations.Migration):
dependencies = [
('account', '0004_remove_userprofile_time_zone'),
]
operations = [
migrations.RenameField(
model_name='userprofile',
old_name='problems_status',
new_name='acm_problems_status',
),
migrations.AddField(
model_name='userprofile',
name='oi_problems_status',
field=jsonfield.fields.JSONField(default={}),
),
migrations.RemoveField(
model_name='user',
name='real_name',
),
migrations.RemoveField(
model_name='userprofile',
name='student_id',
),
migrations.AddField(
model_name='userprofile',
name='real_name',
field=models.CharField(max_length=30, blank=True, null=True),
),
]

View File

@ -24,7 +24,6 @@ class UserManager(models.Manager):
class User(AbstractBaseUser): class User(AbstractBaseUser):
username = models.CharField(max_length=30, unique=True) username = models.CharField(max_length=30, unique=True)
real_name = models.CharField(max_length=30, null=True)
email = models.EmailField(max_length=254, null=True) email = models.EmailField(max_length=254, null=True)
create_time = models.DateTimeField(auto_now_add=True, null=True) create_time = models.DateTimeField(auto_now_add=True, null=True)
# One of UserType # One of UserType
@ -69,17 +68,19 @@ def _random_avatar():
class UserProfile(models.Model): class UserProfile(models.Model):
user = models.OneToOneField(User) user = models.OneToOneField(User)
# Store user problem solution status with json string format, Only for problems not contest_problems # Store user problem solution status with json string format
# ACM: {1: {status: JudgeStatus.ACCEPTED}} # {problems: {1: JudgeStatus.ACCEPTED}, contest_problems: {1: JudgeStatus.ACCEPTED}}, record problem_id and status
# OI: {1: {score: 33}} acm_problems_status = JSONField(default={})
problems_status = JSONField(default={}) # {problems: {1: 33}, contest_problems: {1: 44}, record problem_id and score
oi_problems_status = JSONField(default={})
real_name = models.CharField(max_length=30, blank=True, null=True)
avatar = models.CharField(max_length=50, default=_random_avatar) avatar = models.CharField(max_length=50, default=_random_avatar)
blog = models.URLField(blank=True, null=True) blog = models.URLField(blank=True, null=True)
mood = models.CharField(max_length=200, blank=True, null=True) mood = models.CharField(max_length=200, blank=True, null=True)
phone_number = models.CharField(max_length=15, blank=True, null=True) phone_number = models.CharField(max_length=15, blank=True, null=True)
school = models.CharField(max_length=200, blank=True, null=True) school = models.CharField(max_length=200, blank=True, null=True)
major = 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)
language = models.CharField(max_length=32, blank=True, null=True) language = models.CharField(max_length=32, blank=True, null=True)
# for ACM # for ACM
accepted_number = models.IntegerField(default=0) accepted_number = models.IntegerField(default=0)

View File

@ -35,18 +35,23 @@ class UserSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = User model = User
fields = ["id", "username", "real_name", "email", "admin_type", "problem_permission", fields = ["id", "username", "email", "admin_type", "problem_permission",
"create_time", "last_login", "two_factor_auth", "open_api", "is_disabled"] "create_time", "last_login", "two_factor_auth", "open_api", "is_disabled"]
class UserProfileSerializer(serializers.ModelSerializer): class UserProfileSerializer(serializers.ModelSerializer):
user = UserSerializer() user = UserSerializer()
acm_problems_status = serializers.JSONField()
oi_problems_status = serializers.JSONField()
class Meta: class Meta:
model = UserProfile model = UserProfile
class UserInfoSerializer(serializers.ModelSerializer): class UserInfoSerializer(serializers.ModelSerializer):
acm_problems_status = serializers.JSONField()
oi_problems_status = serializers.JSONField()
class Meta: class Meta:
model = UserProfile model = UserProfile
@ -54,7 +59,6 @@ class UserInfoSerializer(serializers.ModelSerializer):
class EditUserSerializer(serializers.Serializer): class EditUserSerializer(serializers.Serializer):
id = serializers.IntegerField() id = serializers.IntegerField()
username = serializers.CharField(max_length=30) username = serializers.CharField(max_length=30)
real_name = serializers.CharField(max_length=30)
password = serializers.CharField(max_length=30, min_length=6, allow_blank=True, required=False, default=None) password = serializers.CharField(max_length=30, min_length=6, allow_blank=True, required=False, default=None)
email = serializers.EmailField(max_length=254) email = serializers.EmailField(max_length=254)
admin_type = serializers.ChoiceField(choices=(AdminType.REGULAR_USER, AdminType.ADMIN, AdminType.SUPER_ADMIN)) admin_type = serializers.ChoiceField(choices=(AdminType.REGULAR_USER, AdminType.ADMIN, AdminType.SUPER_ADMIN))
@ -66,13 +70,13 @@ class EditUserSerializer(serializers.Serializer):
class EditUserProfileSerializer(serializers.Serializer): class EditUserProfileSerializer(serializers.Serializer):
real_name = serializers.CharField(max_length=30)
avatar = serializers.CharField(max_length=100, allow_null=True, required=False) avatar = serializers.CharField(max_length=100, allow_null=True, required=False)
blog = serializers.URLField(allow_null=True, required=False) blog = serializers.URLField(allow_null=True, required=False)
mood = serializers.CharField(max_length=200, allow_null=True, required=False) mood = serializers.CharField(max_length=200, allow_null=True, required=False)
phone_number = serializers.CharField(max_length=15, allow_null=True, required=False, ) phone_number = serializers.CharField(max_length=15, allow_null=True, required=False, )
school = serializers.CharField(max_length=200, allow_null=True, required=False) school = serializers.CharField(max_length=200, allow_null=True, required=False)
major = serializers.CharField(max_length=200, allow_null=True, required=False) major = serializers.CharField(max_length=200, allow_null=True, required=False)
student_id = serializers.CharField(max_length=15, allow_null=True, required=False)
class ApplyResetPasswordSerializer(serializers.Serializer): class ApplyResetPasswordSerializer(serializers.Serializer):

View File

@ -1,20 +1,19 @@
import os import os
import qrcode import qrcode
from io import BytesIO
from datetime import timedelta from datetime import timedelta
from otpauth import OtpAuth from otpauth import OtpAuth
from django.conf import settings from django.conf import settings
from django.contrib import auth from django.contrib import auth
from django.utils.timezone import now from django.utils.timezone import now
from django.http import HttpResponse
from django.views.decorators.csrf import ensure_csrf_cookie from django.views.decorators.csrf import ensure_csrf_cookie
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.template.loader import render_to_string
from conf.models import WebsiteConfig from conf.models import WebsiteConfig
from utils.api import APIView, validate_serializer, CSRFExemptAPIView from utils.api import APIView, validate_serializer, CSRFExemptAPIView
from utils.captcha import Captcha from utils.captcha import Captcha
from utils.shortcuts import rand_str from utils.shortcuts import rand_str, img2base64
from ..decorators import login_required from ..decorators import login_required
from ..models import User, UserProfile from ..models import User, UserProfile
@ -29,16 +28,14 @@ from ..tasks import send_email_async
class UserProfileAPI(APIView): class UserProfileAPI(APIView):
"""
判断是否登录 若登录返回用户信息
"""
@method_decorator(ensure_csrf_cookie) @method_decorator(ensure_csrf_cookie)
def get(self, request, **kwargs): def get(self, request, **kwargs):
"""
判断是否登录 若登录返回用户信息
"""
user = request.user user = request.user
if not user.is_authenticated(): if not user.is_authenticated():
return self.success(0) return self.success(0)
username = request.GET.get("username") username = request.GET.get("username")
try: try:
if username: if username:
@ -55,19 +52,10 @@ class UserProfileAPI(APIView):
def put(self, request): def put(self, request):
data = request.data data = request.data
user_profile = request.user.userprofile user_profile = request.user.userprofile
print(data) for k, v in data.items():
if data.get("avatar"): setattr(user_profile, k, v)
user_profile.avatar = data["avatar"]
else:
user_profile.mood = data["mood"]
user_profile.blog = data["blog"]
user_profile.school = data["school"]
user_profile.student_id = data["student_id"]
user_profile.phone_number = data["phone_number"]
user_profile.major = data["major"]
# Timezone & language 暂时不加
user_profile.save() user_profile.save()
return self.success("Succeeded") return self.success(UserProfileSerializer(user_profile).data)
class AvatarUploadAPI(CSRFExemptAPIView): class AvatarUploadAPI(CSRFExemptAPIView):
@ -137,11 +125,9 @@ class TwoFactorAuthAPI(APIView):
user.save() user.save()
config = WebsiteConfig.objects.first() config = WebsiteConfig.objects.first()
image = qrcode.make(OtpAuth(token).to_uri("totp", config.base_url, config.name)) label = f"{config.name_shortcut}:{user.username}@{config.base_url}"
buf = BytesIO() image = qrcode.make(OtpAuth(token).to_uri("totp", label, config.name))
image.save(buf, "gif") return self.success(img2base64(image))
return HttpResponse(buf.getvalue(), "image/gif")
@login_required @login_required
@validate_serializer(TwoFactorAuthCodeSerializer) @validate_serializer(TwoFactorAuthCodeSerializer)
@ -215,17 +201,17 @@ class UsernameOrEmailCheck(APIView):
check username or email is duplicate check username or email is duplicate
""" """
data = request.data data = request.data
# True means OK. # True means already exist.
result = { result = {
"username": True, "username": False,
"email": True "email": False
} }
if data.get("username"): if data.get("username"):
if User.objects.filter(username=data["username"]).exists(): if User.objects.filter(username=data["username"]).exists():
result["username"] = False result["username"] = True
if data.get("email"): if data.get("email"):
if User.objects.filter(email=data["email"]).exists(): if User.objects.filter(email=data["email"]).exists():
result["email"] = False result["email"] = True
return self.success(result) return self.success(result)
@ -259,9 +245,6 @@ class UserChangePasswordAPI(APIView):
User change password api User change password api
""" """
data = request.data data = request.data
captcha = Captcha(request)
if not captcha.check(data["captcha"]):
return self.error("Invalid captcha")
username = request.user.username username = request.user.username
user = auth.authenticate(username=username, password=data["old_password"]) user = auth.authenticate(username=username, password=data["old_password"])
if user: if user:
@ -284,24 +267,23 @@ class ApplyResetPasswordAPI(APIView):
user = User.objects.get(email=data["email"]) user = User.objects.get(email=data["email"])
except User.DoesNotExist: except User.DoesNotExist:
return self.error("User does not exist") return self.error("User does not exist")
if user.reset_password_token_expire_time and 0 < ( if user.reset_password_token_expire_time and \
user.reset_password_token_expire_time - now()).total_seconds() < 20 * 60: 0 < int((user.reset_password_token_expire_time - now()).total_seconds()) < 20 * 60:
return self.error("You can only reset password once per 20 minutes") return self.error("You can only reset password once per 20 minutes")
user.reset_password_token = rand_str() user.reset_password_token = rand_str()
user.reset_password_token_expire_time = now() + timedelta(minutes=20) user.reset_password_token_expire_time = now() + timedelta(minutes=20)
user.save() user.save()
email_template = open("reset_password_email.html", "w", render_data = {
encoding="utf-8").read() "username": user.username,
email_template = email_template.replace("{{ username }}", user.username). \ "website_name": config.name,
replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]). \ "link": f"{config.base_url}/reset-password/{user.reset_password_token}"
replace("{{ link }}", settings.WEBSITE_INFO["url"] + "/reset_password/t/" + }
user.reset_password_token) email_html = render_to_string('reset_password_email.html', render_data)
send_email_async.delay(config.name, send_email_async.delay(config.name,
user.email, user.email,
user.username, user.username,
config.name + " 登录信息找回邮件", config.name + " 登录信息找回邮件",
email_template) email_html)
return self.success("Succeeded") return self.success("Succeeded")
@ -316,8 +298,8 @@ class ResetPasswordAPI(APIView):
user = User.objects.get(reset_password_token=data["token"]) user = User.objects.get(reset_password_token=data["token"])
except User.DoesNotExist: except User.DoesNotExist:
return self.error("Token dose not exist") return self.error("Token dose not exist")
if 0 < (user.reset_password_token_expire_time - now()).total_seconds() < 30 * 60: if int((user.reset_password_token_expire_time - now()).total_seconds()) < 0:
return self.error("Token expired") return self.error("Token have expired")
user.reset_password_token = None user.reset_password_token = None
user.set_password(data["password"]) user.set_password(data["password"])
user.save() user.save()

View File

@ -36,7 +36,7 @@ class JudgeDispatcher(object):
self.redis_conn = judge_cache self.redis_conn = judge_cache
self.submission = Submission.objects.get(pk=submission_id) self.submission = Submission.objects.get(pk=submission_id)
if self.submission.contest_id: if self.submission.contest_id:
self.problem = ContestProblem.objects.select_related("contest")\ self.problem = ContestProblem.objects.select_related("contest") \
.get(_id=problem_id, contest_id=self.submission.contest_id) .get(_id=problem_id, contest_id=self.submission.contest_id)
self.contest = self.problem.contest self.contest = self.problem.contest
else: else:
@ -114,7 +114,8 @@ class JudgeDispatcher(object):
# todo OI statistic_info["score"] # todo OI statistic_info["score"]
error_test_case = list(filter(lambda case: case["result"] != 0, resp["data"])) error_test_case = list(filter(lambda case: case["result"] != 0, resp["data"]))
# 多个测试点全部正确则AC否则 ACM模式下取第一个错误的测试点的状态, OI模式若全部错误则取第一个错误测试点状态否则为部分正确 # ACM模式下,多个测试点全部正确则AC否则取第一个错误的测试点的状态
# OI模式下, 若多个测试点全部正确则AC 若全部错误则取第一个错误测试点状态,否则为部分正确
if not error_test_case: if not error_test_case:
self.submission.result = JudgeStatus.ACCEPTED self.submission.result = JudgeStatus.ACCEPTED
elif self.problem.rule_type == ProblemRuleType.ACM or len(error_test_case) == len(resp["data"]): elif self.problem.rule_type == ProblemRuleType.ACM or len(error_test_case) == len(resp["data"]):
@ -125,11 +126,9 @@ class JudgeDispatcher(object):
self.release_judge_res(server.id) self.release_judge_res(server.id)
self.update_problem_status() self.update_problem_status()
if self.submission.contest_id: if self.submission.contest_id:
self.update_contest_rank() self.update_contest_rank()
else:
self.update_user_profile()
# 至此判题结束,尝试处理任务队列中剩余的任务 # 至此判题结束,尝试处理任务队列中剩余的任务
process_pending_task() process_pending_task()
@ -140,53 +139,71 @@ 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):
self.problem.add_submission_number()
if self.submission.result == JudgeStatus.ACCEPTED:
self.problem.add_ac_number()
with transaction.atomic(): with transaction.atomic():
# prepare problem and user_profile
if self.submission.contest_id: 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(contest_id=self.contest.id,
_id=self.problem._id)
else: else:
problem = Problem.objects.select_related().get(_id=self.problem._id) problem = Problem.objects.select_for_update().get(_id=self.problem._id)
info = problem.statistic_info problem_info = problem.statistic_info
result = str(self.submission.result) user = User.objects.select_for_update().select_for_update("userprofile").get(id=self.submission.user_id)
info[result] = info.get(result, 0) + 1
problem.statistic_info = info
problem.save(update_fields=["statistic_info"])
def update_user_profile(self):
with transaction.atomic():
user = User.objects.select_for_update().get(id=self.submission.user_id)
user_profile = user.userprofile user_profile = user.userprofile
user_profile.add_submission_number() if self.submission.contest_id:
problems_status = user_profile.problems_status 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, {})
# update submission and accepted number counter
# only when submission is not in contest, we update user profile,
# in other words, users' submission in a contest will not be counted in user profile
if not self.submission.contest_id:
user_profile.submission_number += 1
if self.submission.result == JudgeStatus.ACCEPTED:
user_profile.accepted_number += 1
problem.submission_number += 1
if self.submission.result == JudgeStatus.ACCEPTED:
problem.accepted_number += 1
problem_id = str(self.problem._id) problem_id = str(self.problem._id)
if self.problem.rule_type == ProblemRuleType.ACM: if self.problem.rule_type == ProblemRuleType.ACM:
if problem_id not in problems_status: # update acm problem info
problems_status[problem_id] = {"status": self.submission.result} result = str(self.submission.result)
problem_info[result] = problem_info.get(result, 0) + 1
problem.statistic_info = problem_info
# update user_profile
if problem_id not in acm_problems_status:
acm_problems_status[problem_id] = self.submission.result
# skip if the problem has been accepted
elif acm_problems_status[problem_id] != JudgeStatus.ACCEPTED:
if self.submission.result == JudgeStatus.ACCEPTED: if self.submission.result == JudgeStatus.ACCEPTED:
user_profile.add_accepted_problem_number() acm_problems_status[problem_id] = JudgeStatus.ACCEPTED
# 以前提交过, 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: else:
problems_status[problem_id]["status"] = self.submission.result acm_problems_status[problem_id] = self.submission.result
user_profile.acm_problems_status[key] = acm_problems_status
else: else:
# update oi problem info
score = self.submission.statistic_info["score"] score = self.submission.statistic_info["score"]
if problem_id not in problems_status: problem_info[score] = problem_info.get(score, 0) + 1
user_profile.add_score(score) problem.statistic_info = problem_info
problems_status[problem_id] = {"score": score}
else:
# 加上本次 减掉上次的score
user_profile.add_score(score, problems_status[problem_id]["score"])
problems_status[problem_id] = {"score": score}
user_profile.problems_status = problems_status # update user_profile
user_profile.save(update_fields=["problems_status"]) if problem_id not in oi_problems_status:
user_profile.add_score(score)
oi_problems_status[problem_id] = score
else:
# minus last time score, add this time score
user_profile.add_score(score, oi_problems_status[problem_id])
oi_problems_status[problem_id] = score
user_profile.oi_problems_status[key] = oi_problems_status
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.real_time_rank: if self.contest.real_time_rank:

View File

@ -23,7 +23,6 @@ if ENV == "local":
elif ENV == "server": elif ENV == "server":
from .server_settings import * from .server_settings import *
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -80,9 +79,26 @@ TEMPLATES = [
}, },
}, },
] ]
WSGI_APPLICATION = 'oj.wsgi.application' WSGI_APPLICATION = 'oj.wsgi.application'
# Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/ # https://docs.djangoproject.com/en/1.8/topics/i18n/
@ -105,7 +121,7 @@ AUTH_USER_MODEL = 'account.User'
LOGGING = { LOGGING = {
'version': 1, 'version': 1,
'disable_existing_loggers': True, 'disable_existing_loggers': False,
'formatters': { 'formatters': {
'standard': { 'standard': {
'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s'} 'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s'}

View File

@ -57,8 +57,7 @@ class AbstractProblem(models.Model):
source = models.CharField(max_length=200, blank=True, null=True) source = models.CharField(max_length=200, blank=True, null=True)
submission_number = models.BigIntegerField(default=0) submission_number = models.BigIntegerField(default=0)
accepted_number = models.BigIntegerField(default=0) accepted_number = models.BigIntegerField(default=0)
# {0: 0, 1: 0, 2: 0, 3: 0 ...} # ACM rule_type: {JudgeStatus.ACCEPTED: 3, JudgeStaus.WRONG_ANSWER: 11}, the number means count
# the first number means JudgeStatus, the second number present count
statistic_info = JSONField(default={}) statistic_info = JSONField(default={})
class Meta: class Meta:

View File

@ -1,10 +1,10 @@
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
from ..models import ProblemTag, Problem, ContestProblem from ..models import ProblemTag, Problem, ContestProblem, ProblemRuleType
from ..serializers import ProblemSerializer, TagSerializer from ..serializers import ProblemSerializer, TagSerializer
from ..serializers import ContestProblemSerializer from ..serializers import ContestProblemSerializer
from contest.models import ContestRuleType
class ProblemTagAPI(APIView): class ProblemTagAPI(APIView):
def get(self, request): def get(self, request):
@ -41,8 +41,18 @@ class ProblemAPI(APIView):
difficulty_rank = request.GET.get("difficulty") difficulty_rank = request.GET.get("difficulty")
if difficulty_rank: if difficulty_rank:
problems = problems.filter(difficulty=difficulty_rank) problems = problems.filter(difficulty=difficulty_rank)
# 根据profile 为做过的题目添加标记
return self.success(self.paginate_data(request, problems, ProblemSerializer)) data = self.paginate_data(request, problems, ProblemSerializer)
if request.user.id:
profile = request.user.userprofile
acm_problems_status = profile.acm_problems_status.get("problems", {})
oi_problems_status = profile.oi_problems_status.get("problems", {})
for problem in data["results"]:
if problem["rule_type"] == ProblemRuleType.ACM:
problem["my_status"] = acm_problems_status.get(problem["_id"], None)
else:
problem["my_status"] = oi_problems_status.get(problem["_id"], None)
return self.success(data)
class ContestProblemAPI(APIView): class ContestProblemAPI(APIView):
@ -57,4 +67,14 @@ class ContestProblemAPI(APIView):
return self.success(ContestProblemSerializer(problem).data) return self.success(ContestProblemSerializer(problem).data)
contest_problems = ContestProblem.objects.select_related("created_by").filter(contest=self.contest, visible=True) contest_problems = ContestProblem.objects.select_related("created_by").filter(contest=self.contest, visible=True)
# 根据profile 为做过的题目添加标记
data = ContestProblemSerializer(contest_problems, many=True).data
if request.user.id:
profile = request.user.userprofile
if self.contest.rule_type == ContestRuleType.ACM:
problems_status = profile.acm_problems_status.get("contest_problems", {})
else:
problems_status = profile.oi_problems_status.get("contest_problems", {})
for problem in data:
problem["my_status"] = problems_status.get(problem["_id"], None)
return self.success(ContestProblemSerializer(contest_problems, many=True).data) return self.success(ContestProblemSerializer(contest_problems, many=True).data)

View File

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-08-30 11:54
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('submission', '0005_submission_username'),
]
operations = [
migrations.AlterField(
model_name='submission',
name='result',
field=models.IntegerField(db_index=True, default=6),
),
]

View File

@ -27,7 +27,7 @@ class Submission(models.Model):
user_id = models.IntegerField(db_index=True) user_id = models.IntegerField(db_index=True)
username = models.CharField(max_length=30) username = models.CharField(max_length=30)
code = models.TextField() code = models.TextField()
result = models.IntegerField(default=JudgeStatus.PENDING) result = models.IntegerField(db_index=True, default=JudgeStatus.PENDING)
# 判题结果的详细信息 # 判题结果的详细信息
info = JSONField(default={}) info = JSONField(default={})
language = models.CharField(max_length=20) language = models.CharField(max_length=20)

View File

@ -1,7 +1,7 @@
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, ContestProblem from problem.models import Problem, ProblemRuleType, ContestProblem
from contest.models import Contest, ContestStatus from contest.models import Contest, ContestStatus
from utils.api import APIView, validate_serializer from utils.api import APIView, validate_serializer
@ -104,11 +104,14 @@ class SubmissionListAPI(APIView):
def process_submissions(self, request, submissions): def process_submissions(self, request, submissions):
problem_id = request.GET.get("problem_id") problem_id = request.GET.get("problem_id")
myself = request.GET.get("myself")
result = request.GET.get("result")
if problem_id: if problem_id:
submissions = submissions.filter(problem_id=problem_id) submissions = submissions.filter(problem_id=problem_id)
if myself and myself == "1":
if request.GET.get("myself") and request.GET["myself"] == "1":
submissions = submissions.filter(user_id=request.user.id) submissions = submissions.filter(user_id=request.user.id)
if result:
submissions = submissions.filter(result=result)
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)

View File

@ -82,10 +82,7 @@ class Captcha(object):
x += font_size * random.randrange(6, 8) / 10 x += font_size * random.randrange(6, 8) / 10
self.django_request.session[self.session_key] = "".join(code) self.django_request.session[self.session_key] = "".join(code)
with BytesIO() as buf: return image
image.save(buf, "gif")
buf_str = buf.getvalue()
return buf_str
def check(self, code): def check(self, code):
""" """

View File

@ -2,10 +2,9 @@ from base64 import b64encode
from . import Captcha from . import Captcha
from ..api import APIView from ..api import APIView
from ..shortcuts import img2base64
class CaptchaAPIView(APIView): class CaptchaAPIView(APIView):
def get(self, request): def get(self, request):
img_prefix = "data:image/png;base64," return self.success(img2base64(Captcha(request).get()))
img = img_prefix + b64encode(Captcha(request).get()).decode("utf-8")
return self.success(img)

View File

@ -1,5 +1,7 @@
import logging import logging
import random import random
from io import BytesIO
from base64 import b64encode
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from envelopes import Envelope from envelopes import Envelope
@ -58,3 +60,12 @@ def build_query_string(kv_data, ignore_none=True):
query_string = "?" query_string = "?"
query_string += (k + "=" + str(v)) query_string += (k + "=" + str(v))
return query_string return query_string
def img2base64(img):
with BytesIO() as buf:
img.save(buf, "gif")
buf_str = buf.getvalue()
img_prefix = "data:image/png;base64,"
b64_str = img_prefix + b64encode(buf_str).decode("utf-8")
return b64_str