mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2025-01-16 17:26:38 +00:00
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:
parent
1e4ede6d1a
commit
f55a242ec0
39
account/migrations/0005_auto_20170830_1154.py
Normal file
39
account/migrations/0005_auto_20170830_1154.py
Normal 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),
|
||||
),
|
||||
]
|
@ -24,7 +24,6 @@ class UserManager(models.Manager):
|
||||
|
||||
class User(AbstractBaseUser):
|
||||
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)
|
||||
create_time = models.DateTimeField(auto_now_add=True, null=True)
|
||||
# One of UserType
|
||||
@ -69,17 +68,19 @@ def _random_avatar():
|
||||
|
||||
class UserProfile(models.Model):
|
||||
user = models.OneToOneField(User)
|
||||
# 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={})
|
||||
# Store user problem solution status with json string format
|
||||
# {problems: {1: JudgeStatus.ACCEPTED}, contest_problems: {1: JudgeStatus.ACCEPTED}}, record problem_id and status
|
||||
acm_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)
|
||||
blog = models.URLField(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)
|
||||
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)
|
||||
language = models.CharField(max_length=32, blank=True, null=True)
|
||||
# for ACM
|
||||
accepted_number = models.IntegerField(default=0)
|
||||
|
@ -35,18 +35,23 @@ class UserSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
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"]
|
||||
|
||||
|
||||
class UserProfileSerializer(serializers.ModelSerializer):
|
||||
user = UserSerializer()
|
||||
acm_problems_status = serializers.JSONField()
|
||||
oi_problems_status = serializers.JSONField()
|
||||
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
|
||||
|
||||
class UserInfoSerializer(serializers.ModelSerializer):
|
||||
acm_problems_status = serializers.JSONField()
|
||||
oi_problems_status = serializers.JSONField()
|
||||
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
|
||||
@ -54,7 +59,6 @@ class UserInfoSerializer(serializers.ModelSerializer):
|
||||
class EditUserSerializer(serializers.Serializer):
|
||||
id = serializers.IntegerField()
|
||||
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)
|
||||
email = serializers.EmailField(max_length=254)
|
||||
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):
|
||||
real_name = serializers.CharField(max_length=30)
|
||||
avatar = serializers.CharField(max_length=100, allow_null=True, required=False)
|
||||
blog = serializers.URLField(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, )
|
||||
school = 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):
|
||||
|
@ -1,20 +1,19 @@
|
||||
import os
|
||||
import qrcode
|
||||
from io import BytesIO
|
||||
from datetime import timedelta
|
||||
from otpauth import OtpAuth
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import auth
|
||||
from django.utils.timezone import now
|
||||
from django.http import HttpResponse
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from conf.models import WebsiteConfig
|
||||
from utils.api import APIView, validate_serializer, CSRFExemptAPIView
|
||||
from utils.captcha import Captcha
|
||||
from utils.shortcuts import rand_str
|
||||
from utils.shortcuts import rand_str, img2base64
|
||||
|
||||
from ..decorators import login_required
|
||||
from ..models import User, UserProfile
|
||||
@ -29,16 +28,14 @@ from ..tasks import send_email_async
|
||||
|
||||
|
||||
class UserProfileAPI(APIView):
|
||||
"""
|
||||
判断是否登录, 若登录返回用户信息
|
||||
"""
|
||||
|
||||
@method_decorator(ensure_csrf_cookie)
|
||||
def get(self, request, **kwargs):
|
||||
"""
|
||||
判断是否登录, 若登录返回用户信息
|
||||
"""
|
||||
user = request.user
|
||||
if not user.is_authenticated():
|
||||
return self.success(0)
|
||||
|
||||
username = request.GET.get("username")
|
||||
try:
|
||||
if username:
|
||||
@ -55,19 +52,10 @@ class UserProfileAPI(APIView):
|
||||
def put(self, request):
|
||||
data = request.data
|
||||
user_profile = request.user.userprofile
|
||||
print(data)
|
||||
if data.get("avatar"):
|
||||
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 暂时不加
|
||||
for k, v in data.items():
|
||||
setattr(user_profile, k, v)
|
||||
user_profile.save()
|
||||
return self.success("Succeeded")
|
||||
return self.success(UserProfileSerializer(user_profile).data)
|
||||
|
||||
|
||||
class AvatarUploadAPI(CSRFExemptAPIView):
|
||||
@ -137,11 +125,9 @@ class TwoFactorAuthAPI(APIView):
|
||||
user.save()
|
||||
|
||||
config = WebsiteConfig.objects.first()
|
||||
image = qrcode.make(OtpAuth(token).to_uri("totp", config.base_url, config.name))
|
||||
buf = BytesIO()
|
||||
image.save(buf, "gif")
|
||||
|
||||
return HttpResponse(buf.getvalue(), "image/gif")
|
||||
label = f"{config.name_shortcut}:{user.username}@{config.base_url}"
|
||||
image = qrcode.make(OtpAuth(token).to_uri("totp", label, config.name))
|
||||
return self.success(img2base64(image))
|
||||
|
||||
@login_required
|
||||
@validate_serializer(TwoFactorAuthCodeSerializer)
|
||||
@ -215,17 +201,17 @@ class UsernameOrEmailCheck(APIView):
|
||||
check username or email is duplicate
|
||||
"""
|
||||
data = request.data
|
||||
# True means OK.
|
||||
# True means already exist.
|
||||
result = {
|
||||
"username": True,
|
||||
"email": True
|
||||
"username": False,
|
||||
"email": False
|
||||
}
|
||||
if data.get("username"):
|
||||
if User.objects.filter(username=data["username"]).exists():
|
||||
result["username"] = False
|
||||
result["username"] = True
|
||||
if data.get("email"):
|
||||
if User.objects.filter(email=data["email"]).exists():
|
||||
result["email"] = False
|
||||
result["email"] = True
|
||||
return self.success(result)
|
||||
|
||||
|
||||
@ -259,9 +245,6 @@ class UserChangePasswordAPI(APIView):
|
||||
User change password api
|
||||
"""
|
||||
data = request.data
|
||||
captcha = Captcha(request)
|
||||
if not captcha.check(data["captcha"]):
|
||||
return self.error("Invalid captcha")
|
||||
username = request.user.username
|
||||
user = auth.authenticate(username=username, password=data["old_password"])
|
||||
if user:
|
||||
@ -284,24 +267,23 @@ class ApplyResetPasswordAPI(APIView):
|
||||
user = User.objects.get(email=data["email"])
|
||||
except User.DoesNotExist:
|
||||
return self.error("User does not exist")
|
||||
if user.reset_password_token_expire_time and 0 < (
|
||||
user.reset_password_token_expire_time - now()).total_seconds() < 20 * 60:
|
||||
if user.reset_password_token_expire_time and \
|
||||
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")
|
||||
user.reset_password_token = rand_str()
|
||||
|
||||
user.reset_password_token_expire_time = now() + timedelta(minutes=20)
|
||||
user.save()
|
||||
email_template = open("reset_password_email.html", "w",
|
||||
encoding="utf-8").read()
|
||||
email_template = email_template.replace("{{ username }}", user.username). \
|
||||
replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]). \
|
||||
replace("{{ link }}", settings.WEBSITE_INFO["url"] + "/reset_password/t/" +
|
||||
user.reset_password_token)
|
||||
render_data = {
|
||||
"username": user.username,
|
||||
"website_name": config.name,
|
||||
"link": f"{config.base_url}/reset-password/{user.reset_password_token}"
|
||||
}
|
||||
email_html = render_to_string('reset_password_email.html', render_data)
|
||||
send_email_async.delay(config.name,
|
||||
user.email,
|
||||
user.username,
|
||||
config.name + " 登录信息找回邮件",
|
||||
email_template)
|
||||
email_html)
|
||||
return self.success("Succeeded")
|
||||
|
||||
|
||||
@ -316,8 +298,8 @@ class ResetPasswordAPI(APIView):
|
||||
user = User.objects.get(reset_password_token=data["token"])
|
||||
except User.DoesNotExist:
|
||||
return self.error("Token dose not exist")
|
||||
if 0 < (user.reset_password_token_expire_time - now()).total_seconds() < 30 * 60:
|
||||
return self.error("Token expired")
|
||||
if int((user.reset_password_token_expire_time - now()).total_seconds()) < 0:
|
||||
return self.error("Token have expired")
|
||||
user.reset_password_token = None
|
||||
user.set_password(data["password"])
|
||||
user.save()
|
||||
|
@ -36,7 +36,7 @@ class JudgeDispatcher(object):
|
||||
self.redis_conn = judge_cache
|
||||
self.submission = Submission.objects.get(pk=submission_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)
|
||||
self.contest = self.problem.contest
|
||||
else:
|
||||
@ -114,7 +114,8 @@ class JudgeDispatcher(object):
|
||||
# todo OI statistic_info["score"]
|
||||
|
||||
error_test_case = list(filter(lambda case: case["result"] != 0, resp["data"]))
|
||||
# 多个测试点全部正确则AC,否则 ACM模式下取第一个错误的测试点的状态, OI模式若全部错误则取第一个错误测试点状态,否则为部分正确
|
||||
# ACM模式下,多个测试点全部正确则AC,否则取第一个错误的测试点的状态
|
||||
# OI模式下, 若多个测试点全部正确则AC, 若全部错误则取第一个错误测试点状态,否则为部分正确
|
||||
if not error_test_case:
|
||||
self.submission.result = JudgeStatus.ACCEPTED
|
||||
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.update_problem_status()
|
||||
|
||||
if self.submission.contest_id:
|
||||
self.update_contest_rank()
|
||||
else:
|
||||
self.update_user_profile()
|
||||
|
||||
# 至此判题结束,尝试处理任务队列中剩余的任务
|
||||
process_pending_task()
|
||||
|
||||
@ -140,53 +139,71 @@ class JudgeDispatcher(object):
|
||||
return self._request(urljoin(service_url, "compile_spj"), data=data)
|
||||
|
||||
def update_problem_status(self):
|
||||
self.problem.add_submission_number()
|
||||
if self.submission.result == JudgeStatus.ACCEPTED:
|
||||
self.problem.add_ac_number()
|
||||
with transaction.atomic():
|
||||
# prepare problem and user_profile
|
||||
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:
|
||||
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
|
||||
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)
|
||||
problem = Problem.objects.select_for_update().get(_id=self.problem._id)
|
||||
problem_info = problem.statistic_info
|
||||
user = User.objects.select_for_update().select_for_update("userprofile").get(id=self.submission.user_id)
|
||||
user_profile = user.userprofile
|
||||
user_profile.add_submission_number()
|
||||
problems_status = user_profile.problems_status
|
||||
if self.submission.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, {})
|
||||
|
||||
# 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)
|
||||
if self.problem.rule_type == ProblemRuleType.ACM:
|
||||
if problem_id not in problems_status:
|
||||
problems_status[problem_id] = {"status": self.submission.result}
|
||||
# update acm problem info
|
||||
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:
|
||||
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
|
||||
acm_problems_status[problem_id] = JudgeStatus.ACCEPTED
|
||||
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:
|
||||
# update oi problem info
|
||||
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:
|
||||
# 加上本次 减掉上次的score
|
||||
user_profile.add_score(score, problems_status[problem_id]["score"])
|
||||
problems_status[problem_id] = {"score": score}
|
||||
problem_info[score] = problem_info.get(score, 0) + 1
|
||||
problem.statistic_info = problem_info
|
||||
|
||||
user_profile.problems_status = problems_status
|
||||
user_profile.save(update_fields=["problems_status"])
|
||||
# update user_profile
|
||||
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):
|
||||
if self.contest.real_time_rank:
|
||||
|
@ -23,7 +23,6 @@ if ENV == "local":
|
||||
elif ENV == "server":
|
||||
from .server_settings import *
|
||||
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
@ -80,9 +79,26 @@ TEMPLATES = [
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
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
|
||||
# https://docs.djangoproject.com/en/1.8/topics/i18n/
|
||||
|
||||
@ -105,7 +121,7 @@ AUTH_USER_MODEL = 'account.User'
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': True,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'standard': {
|
||||
'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s'}
|
||||
|
@ -57,8 +57,7 @@ class AbstractProblem(models.Model):
|
||||
source = models.CharField(max_length=200, blank=True, null=True)
|
||||
submission_number = models.BigIntegerField(default=0)
|
||||
accepted_number = models.BigIntegerField(default=0)
|
||||
# {0: 0, 1: 0, 2: 0, 3: 0 ...}
|
||||
# the first number means JudgeStatus, the second number present count
|
||||
# ACM rule_type: {JudgeStatus.ACCEPTED: 3, JudgeStaus.WRONG_ANSWER: 11}, the number means count
|
||||
statistic_info = JSONField(default={})
|
||||
|
||||
class Meta:
|
||||
|
@ -1,10 +1,10 @@
|
||||
from django.db.models import Q
|
||||
from utils.api import APIView
|
||||
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 ContestProblemSerializer
|
||||
|
||||
from contest.models import ContestRuleType
|
||||
|
||||
class ProblemTagAPI(APIView):
|
||||
def get(self, request):
|
||||
@ -41,8 +41,18 @@ class ProblemAPI(APIView):
|
||||
difficulty_rank = request.GET.get("difficulty")
|
||||
if difficulty_rank:
|
||||
problems = problems.filter(difficulty=difficulty_rank)
|
||||
|
||||
return self.success(self.paginate_data(request, problems, ProblemSerializer))
|
||||
# 根据profile 为做过的题目添加标记
|
||||
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):
|
||||
@ -57,4 +67,14 @@ class ContestProblemAPI(APIView):
|
||||
return self.success(ContestProblemSerializer(problem).data)
|
||||
|
||||
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)
|
||||
|
0
reset_password_email.html
Normal file
0
reset_password_email.html
Normal file
20
submission/migrations/0006_auto_20170830_1154.py
Normal file
20
submission/migrations/0006_auto_20170830_1154.py
Normal 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),
|
||||
),
|
||||
]
|
@ -27,7 +27,7 @@ class Submission(models.Model):
|
||||
user_id = models.IntegerField(db_index=True)
|
||||
username = models.CharField(max_length=30)
|
||||
code = models.TextField()
|
||||
result = models.IntegerField(default=JudgeStatus.PENDING)
|
||||
result = models.IntegerField(db_index=True, default=JudgeStatus.PENDING)
|
||||
# 判题结果的详细信息
|
||||
info = JSONField(default={})
|
||||
language = models.CharField(max_length=20)
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
from account.decorators import login_required, check_contest_permission
|
||||
from judge.tasks import judge_task
|
||||
# from judge.dispatcher import JudgeDispatcher
|
||||
from judge.dispatcher import JudgeDispatcher
|
||||
from problem.models import Problem, ProblemRuleType, ContestProblem
|
||||
from contest.models import Contest, ContestStatus
|
||||
from utils.api import APIView, validate_serializer
|
||||
@ -104,11 +104,14 @@ class SubmissionListAPI(APIView):
|
||||
|
||||
def process_submissions(self, request, submissions):
|
||||
problem_id = request.GET.get("problem_id")
|
||||
myself = request.GET.get("myself")
|
||||
result = request.GET.get("result")
|
||||
if problem_id:
|
||||
submissions = submissions.filter(problem_id=problem_id)
|
||||
|
||||
if request.GET.get("myself") and request.GET["myself"] == "1":
|
||||
if myself and myself == "1":
|
||||
submissions = submissions.filter(user_id=request.user.id)
|
||||
if result:
|
||||
submissions = submissions.filter(result=result)
|
||||
data = self.paginate_data(request, submissions)
|
||||
data["results"] = SubmissionListSerializer(data["results"], many=True, user=request.user).data
|
||||
return self.success(data)
|
||||
|
@ -82,10 +82,7 @@ class Captcha(object):
|
||||
x += font_size * random.randrange(6, 8) / 10
|
||||
|
||||
self.django_request.session[self.session_key] = "".join(code)
|
||||
with BytesIO() as buf:
|
||||
image.save(buf, "gif")
|
||||
buf_str = buf.getvalue()
|
||||
return buf_str
|
||||
return image
|
||||
|
||||
def check(self, code):
|
||||
"""
|
||||
|
@ -2,10 +2,9 @@ from base64 import b64encode
|
||||
|
||||
from . import Captcha
|
||||
from ..api import APIView
|
||||
from ..shortcuts import img2base64
|
||||
|
||||
|
||||
class CaptchaAPIView(APIView):
|
||||
def get(self, request):
|
||||
img_prefix = "data:image/png;base64,"
|
||||
img = img_prefix + b64encode(Captcha(request).get()).decode("utf-8")
|
||||
return self.success(img)
|
||||
return self.success(img2base64(Captcha(request).get()))
|
||||
|
@ -1,5 +1,7 @@
|
||||
import logging
|
||||
import random
|
||||
from io import BytesIO
|
||||
from base64 import b64encode
|
||||
|
||||
from django.utils.crypto import get_random_string
|
||||
from envelopes import Envelope
|
||||
@ -58,3 +60,12 @@ def build_query_string(kv_data, ignore_none=True):
|
||||
query_string = "?"
|
||||
query_string += (k + "=" + str(v))
|
||||
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
|
Loading…
x
Reference in New Issue
Block a user