From e9c734481560909a47820d4d1dd9d9f5d3e98fb9 Mon Sep 17 00:00:00 2001 From: zema1 Date: Fri, 22 Sep 2017 16:41:29 +0800 Subject: [PATCH] adjust account fields, cache the website_config --- account/migrations/0007_auto_20170920_0254.py | 30 +++++++++++++++++++ account/models.py | 4 +-- account/serializers.py | 2 +- account/tests.py | 19 ++++++++++++ account/views/oj.py | 22 +++++++++++++- conf/tests.py | 2 +- conf/views.py | 15 ++++++++-- problem/views/oj.py | 6 ++-- utils/constants.py | 1 + 9 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 account/migrations/0007_auto_20170920_0254.py diff --git a/account/migrations/0007_auto_20170920_0254.py b/account/migrations/0007_auto_20170920_0254.py new file mode 100644 index 00000000..896d0d89 --- /dev/null +++ b/account/migrations/0007_auto_20170920_0254.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-09-20 02:54 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0006_user_session_keys'), + ] + + operations = [ + migrations.RenameField( + model_name='userprofile', + old_name='phone_number', + new_name='github', + ), + migrations.AlterField( + model_name='userprofile', + name='avatar', + field=models.CharField(default='/static/avatar/default.png', max_length=50), + ), + migrations.AlterField( + model_name='userprofile', + name='github', + field=models.CharField(blank=True, max_length=50, null=True), + ), + ] diff --git a/account/models.py b/account/models.py index 6bf4a19f..b909f932 100644 --- a/account/models.py +++ b/account/models.py @@ -76,10 +76,10 @@ class UserProfile(models.Model): oi_problems_status = JSONField(default={}) real_name = models.CharField(max_length=30, blank=True, null=True) - avatar = models.CharField(max_length=50, default=_default_avatar) + avatar = models.CharField(max_length=50, default=_default_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) + github = models.CharField(max_length=50, blank=True, null=True) school = models.CharField(max_length=200, blank=True, null=True) major = models.CharField(max_length=200, blank=True, null=True) language = models.CharField(max_length=32, blank=True, null=True) diff --git a/account/serializers.py b/account/serializers.py index 2b71ceb8..8345fc69 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -74,7 +74,7 @@ class EditUserProfileSerializer(serializers.Serializer): avatar = serializers.CharField(max_length=100, allow_blank=True, required=False) blog = serializers.URLField(allow_blank=True, required=False) mood = serializers.CharField(max_length=200, allow_blank=True, required=False) - phone_number = serializers.CharField(max_length=15, allow_blank=True, required=False, ) + github = serializers.CharField(max_length=50, allow_blank=True, required=False) school = serializers.CharField(max_length=200, allow_blank=True, required=False) major = serializers.CharField(max_length=200, allow_blank=True, required=False) diff --git a/account/tests.py b/account/tests.py index e648e763..edf25b8a 100644 --- a/account/tests.py +++ b/account/tests.py @@ -8,8 +8,11 @@ from otpauth import OtpAuth from utils.api.tests import APIClient, APITestCase from utils.shortcuts import rand_str +from utils.cache import default_cache +from utils.constants import CacheKey from .models import AdminType, ProblemPermission, User +from conf.models import WebsiteConfig class PermissionDecoratorTest(APITestCase): @@ -128,6 +131,13 @@ class UserLoginAPITest(APITestCase): user = auth.get_user(self.client) self.assertFalse(user.is_authenticated()) + def test_user_disabled(self): + self.user.is_disabled = True + self.user.save() + resp = self.client.post(self.login_url, data={"username": self.username, + "password": self.password}) + self.assertDictEqual(resp.data, {"error": "error", "data": "Your account have been disabled"}) + class CaptchaTest(APITestCase): def _set_captcha(self, session): @@ -147,6 +157,15 @@ class UserRegisterAPITest(CaptchaTest): self.data = {"username": "test_user", "password": "testuserpassword", "real_name": "real_name", "email": "test@qduoj.com", "captcha": self._set_captcha(self.client.session)} + # clea cache in redis + default_cache.delete(CacheKey.website_config) + + def test_website_config_limit(self): + website = WebsiteConfig.objects.create() + website.allow_register = False + website.save() + resp = self.client.post(self.register_url, data=self.data) + self.assertDictEqual(resp.data, {"error": "error", "data": "Register have been disabled by admin"}) def test_invalid_captcha(self): self.data["captcha"] = "****" diff --git a/account/views/oj.py b/account/views/oj.py index 06d5c9e4..5b74ca88 100644 --- a/account/views/oj.py +++ b/account/views/oj.py @@ -1,5 +1,6 @@ import os import qrcode +import pickle from datetime import timedelta from otpauth import OtpAuth @@ -15,6 +16,8 @@ from conf.models import WebsiteConfig from utils.api import APIView, validate_serializer from utils.captcha import Captcha from utils.shortcuts import rand_str, img2base64, timestamp2utcstr +from utils.cache import default_cache +from utils.constants import CacheKey from ..decorators import login_required from ..models import User, UserProfile @@ -62,6 +65,7 @@ class UserProfileAPI(APIView): class AvatarUploadAPI(APIView): request_parsers = () + @login_required def post(self, request): form = AvatarUploadForm(request.POST, request.FILES) if form.is_valid(): @@ -195,6 +199,8 @@ class UserLoginAPI(APIView): user = auth.authenticate(username=data["username"], password=data["password"]) # None is returned if username or password is wrong if user: + if user.is_disabled: + return self.error("Your account have been disabled") if not user.two_factor_auth: auth.login(request, user) return self.success("Succeeded") @@ -250,6 +256,18 @@ class UserRegisterAPI(APIView): """ User register api """ + config = default_cache.get(CacheKey.website_config) + if config: + config = pickle.loads(config) + else: + config = WebsiteConfig.objects.first() + if not config: + config = WebsiteConfig.objects.create() + default_cache.set(CacheKey.website_config, pickle.dumps(config)) + + if not config.allow_register: + return self.error("Register have been disabled by admin") + data = request.data captcha = Captcha(request) if not captcha.check(data["captcha"]): @@ -385,7 +403,9 @@ class UserRankAPI(APIView): 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) + profiles = UserProfile.objects.select_related("user")\ + .filter(submission_number__gt=0)\ + .exclude(user__is_disabled=True) if rule_type == "acm": profiles = profiles.order_by("-accepted_number", "submission_number") else: diff --git a/conf/tests.py b/conf/tests.py index fd05b439..ae9ee154 100644 --- a/conf/tests.py +++ b/conf/tests.py @@ -76,7 +76,7 @@ class WebsiteConfigAPITest(APITestCase): url = self.reverse("website_info_api") resp = self.client.get(url) self.assertSuccess(resp) - self.assertEqual(resp.data["data"]["name_shortcut"], "oj") + self.assertEqual(resp.data["data"]["name_shortcut"], "test oj") class JudgeServerHeartbeatTest(APITestCase): diff --git a/conf/views.py b/conf/views.py index c202421f..814c0620 100644 --- a/conf/views.py +++ b/conf/views.py @@ -1,4 +1,5 @@ import hashlib +import pickle from django.utils import timezone @@ -7,6 +8,8 @@ from judge.languages import languages, spj_languages from judge.dispatcher import process_pending_task from utils.api import APIView, CSRFExemptAPIView, validate_serializer from utils.shortcuts import rand_str +from utils.cache import default_cache +from utils.constants import CacheKey from .models import JudgeServer, JudgeServerToken, SMTPConfig, WebsiteConfig from .serializers import (CreateEditWebsiteConfigSerializer, @@ -57,9 +60,14 @@ class SMTPTestAPI(APIView): class WebsiteConfigAPI(APIView): def get(self, request): - config = WebsiteConfig.objects.first() - if not config: - config = WebsiteConfig.objects.create() + config = default_cache.get(CacheKey.website_config) + if config: + config = pickle.loads(config) + else: + config = WebsiteConfig.objects.first() + if not config: + config = WebsiteConfig.objects.create() + default_cache.set(CacheKey.website_config, pickle.dumps(config)) return self.success(WebsiteConfigSerializer(config).data) @validate_serializer(CreateEditWebsiteConfigSerializer) @@ -68,6 +76,7 @@ class WebsiteConfigAPI(APIView): data = request.data WebsiteConfig.objects.all().delete() config = WebsiteConfig.objects.create(**data) + default_cache.set(CacheKey.website_config, pickle.dumps(config)) return self.success(WebsiteConfigSerializer(config).data) diff --git a/problem/views/oj.py b/problem/views/oj.py index 40fb0212..1a724f87 100644 --- a/problem/views/oj.py +++ b/problem/views/oj.py @@ -43,9 +43,9 @@ class ProblemAPI(APIView): problems = problems.filter(Q(title__contains=keyword) | Q(description__contains=keyword)) # 难度筛选 - difficulty_rank = request.GET.get("difficulty") - if difficulty_rank: - problems = problems.filter(difficulty=difficulty_rank) + difficulty = request.GET.get("difficulty") + if difficulty: + problems = problems.filter(difficulty=difficulty) # 根据profile 为做过的题目添加标记 data = self.paginate_data(request, problems, ProblemSerializer) if request.user.id: diff --git a/utils/constants.py b/utils/constants.py index 86181b6c..b11aa216 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -1,3 +1,4 @@ class CacheKey: waiting_queue = "waiting_queue" contest_rank_cache = "contest_rank_cache_" + website_config = "website_config"