From 70f52b6f2718a548be6422ff35097b5f58426049 Mon Sep 17 00:00:00 2001 From: zema1 Date: Thu, 2 Nov 2017 15:29:08 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=90=8D=E4=B8=8D=E5=8C=BA?= =?UTF-8?q?=E5=88=86=E5=A4=A7=E5=B0=8F=E5=86=99=EF=BC=9B=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=9B=B4=E6=96=B0problem=E6=97=B6=E7=9A=84=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- account/models.py | 2 +- account/views/oj.py | 7 +- oj/settings.py | 9 +-- problem/serializers.py | 7 +- problem/views/admin.py | 156 +++++++++++++++++++++++------------------ 5 files changed, 103 insertions(+), 78 deletions(-) diff --git a/account/models.py b/account/models.py index 92e3df38..4f991d7e 100644 --- a/account/models.py +++ b/account/models.py @@ -20,7 +20,7 @@ class UserManager(models.Manager): use_in_migrations = True def get_by_natural_key(self, username): - return self.get(**{self.model.USERNAME_FIELD: username}) + return self.get(**{f"{self.model.USERNAME_FIELD}__iexact": username}) class User(AbstractBaseUser): diff --git a/account/views/oj.py b/account/views/oj.py index abbad3d7..76d363f7 100644 --- a/account/views/oj.py +++ b/account/views/oj.py @@ -198,7 +198,7 @@ class UsernameOrEmailCheck(APIView): if data.get("username"): result["username"] = User.objects.filter(username=data["username"]).exists() if data.get("email"): - result["email"] = User.objects.filter(email=data["email"]).exists() + result["email"] = User.objects.filter(email=data["email"].lower()).exists() return self.success(result) @@ -218,9 +218,9 @@ class UserRegisterAPI(APIView): return self.error("Invalid captcha") if User.objects.filter(username=data["username"]).exists(): return self.error("Username already exists") + data["email"] = data["email"].lower() if User.objects.filter(email=data["email"]).exists(): return self.error("Email already exists") - user = User.objects.create(username=data["username"], email=data["email"]) user.set_password(data["password"]) user.save() @@ -240,6 +240,7 @@ class UserChangeEmailAPI(APIView): return self.error("tfa_required") if not OtpAuth(user.tfa_token).valid_totp(data["tfa_code"]): return self.error("Invalid two factor verification code") + data["new_email"] = data["new_email"].lower() if User.objects.filter(email=data["new_email"]).exists(): return self.error("The email is owned by other account") user.email = data["new_email"] @@ -280,7 +281,7 @@ class ApplyResetPasswordAPI(APIView): if not captcha.check(data["captcha"]): return self.error("Invalid captcha") try: - user = User.objects.get(email=data["email"]) + user = User.objects.get(email__iexact=data["email"]) except User.DoesNotExist: return self.error("User does not exist") if user.reset_password_token_expire_time and 0 < int( diff --git a/oj/settings.py b/oj/settings.py index fd216dd2..0c6e7687 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -118,7 +118,9 @@ LOGGING = { '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'} + 'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s', + 'datefmt': '%Y-%m-%d %H:%M:%S' + } }, 'handlers': { 'django_error': { @@ -145,11 +147,6 @@ LOGGING = { 'level': 'WARNING', 'propagate': True, }, - 'django.server': { - 'handlers': ['django_error', 'console'], - 'level': 'ERROR', - 'propagate': True, - }, 'django.db.backends': { 'handlers': ['django_error', 'console'], 'level': 'WARNING', diff --git a/problem/serializers.py b/problem/serializers.py index 671ed88e..b9ebfd09 100644 --- a/problem/serializers.py +++ b/problem/serializers.py @@ -39,7 +39,7 @@ class CreateOrEditProblemSerializer(serializers.Serializer): input_description = serializers.CharField() output_description = serializers.CharField() samples = serializers.ListField(child=CreateSampleSerializer(), allow_empty=False) - test_case_id = serializers.CharField(min_length=32, max_length=32) + test_case_id = serializers.CharField(max_length=32) test_case_score = serializers.ListField(child=CreateTestCaseScoreSerializer(), allow_empty=False) time_limit = serializers.IntegerField(min_value=1, max_value=1000 * 60) memory_limit = serializers.IntegerField(min_value=1, max_value=1024) @@ -68,6 +68,11 @@ class CreateContestProblemSerializer(CreateOrEditProblemSerializer): contest_id = serializers.IntegerField() +class EditContestProblemSerializer(CreateOrEditProblemSerializer): + id = serializers.IntegerField() + contest_id = serializers.IntegerField() + + class TagSerializer(serializers.ModelSerializer): class Meta: model = ProblemTag diff --git a/problem/views/admin.py b/problem/views/admin.py index 3acd4b52..af7708e2 100644 --- a/problem/views/admin.py +++ b/problem/views/admin.py @@ -12,7 +12,7 @@ from utils.shortcuts import rand_str from ..models import Problem, ProblemRuleType, ProblemTag from ..serializers import (CreateContestProblemSerializer, ContestProblemAdminSerializer, - CreateProblemSerializer, EditProblemSerializer, + CreateProblemSerializer, EditProblemSerializer, EditContestProblemSerializer, ProblemAdminSerializer, TestCaseUploadForm) @@ -109,26 +109,14 @@ class TestCaseUploadAPI(CSRFExemptAPIView): return self.success({"id": test_case_id, "info": ret, "hint": hint, "spj": spj}) -class ProblemAPI(APIView): - @validate_serializer(CreateProblemSerializer) - @problem_permission_required - def post(self, request): +class ProblemBase(APIView): + def common_checks(self, request): data = request.data - - _id = data["_id"] - if _id: - try: - Problem.objects.get(_id=_id) - return self.error("Display ID already exists") - except Problem.DoesNotExist: - pass - else: - data["_id"] = rand_str(8) - if data["spj"]: if not data["spj_language"] or not data["spj_code"]: - return self.error("Invalid spj") - data["spj_version"] = hashlib.md5((data["spj_language"] + ":" + data["spj_code"]).encode("utf-8")).hexdigest() + return "Invalid spj" + data["spj_version"] = hashlib.md5( + (data["spj_language"] + ":" + data["spj_code"]).encode("utf-8")).hexdigest() else: data["spj_language"] = None data["spj_code"] = None @@ -136,21 +124,33 @@ class ProblemAPI(APIView): total_score = 0 for item in data["test_case_score"]: if item["score"] <= 0: - return self.error("Invalid score") + return "Invalid score" else: total_score += item["score"] data["total_score"] = total_score - # todo check filename and score info data["created_by"] = request.user - tags = data.pop("tags") - data["languages"] = list(data["languages"]) - problem = Problem.objects.create(**data) +class ProblemAPI(ProblemBase): + @validate_serializer(CreateProblemSerializer) + @problem_permission_required + def post(self, request): + data = request.data + + _id = data["_id"] if not _id: - problem._id = str(problem.id) - problem.save() + return self.error("Display ID is required") + if Problem.objects.filter(_id=_id, contest_id__isnull=True).exists(): + return self.error("Display ID already exists") + + error_info = self.common_checks(request) + if error_info: + return self.error(error_info) + + # todo check filename and score info + tags = data.pop("tags") + problem = Problem.objects.create(**data) for item in tags: try: @@ -196,31 +196,14 @@ class ProblemAPI(APIView): return self.error("Problem does not exist") _id = data["_id"] - if _id: - try: - Problem.objects.exclude(id=problem_id).get(_id=_id) - return self.error("Display ID already exists") - except Problem.DoesNotExist: - pass - else: - data["_id"] = str(problem_id) + if not _id: + return self.error("Display ID is required") + if Problem.objects.exclude(id=problem_id).filter(_id=_id, contest_id__isnull=True).exists(): + return self.error("Display ID already exists") - if data["spj"]: - if not data["spj_language"] or not data["spj_code"]: - return self.error("Invalid spj") - data["spj_version"] = hashlib.md5((data["spj_language"] + ":" + data["spj_code"]).encode("utf-8")).hexdigest() - else: - data["spj_language"] = None - data["spj_code"] = None - - if data["rule_type"] == ProblemRuleType.OI: - total_score = 0 - for item in data["test_case_score"]: - if item["score"] <= 0: - return self.error("Invalid score") - else: - total_score += item["score"] - data["total_score"] = total_score + error_info = self.common_checks(request) + if error_info: + return self.error(error_info) # todo check filename and score info tags = data.pop("tags") data["languages"] = list(data["languages"]) @@ -240,11 +223,11 @@ class ProblemAPI(APIView): return self.success() -class ContestProblemAPI(APIView): +class ContestProblemAPI(ProblemBase): @validate_serializer(CreateContestProblemSerializer) + @problem_permission_required def post(self, request): data = request.data - try: contest = Contest.objects.get(id=data.pop("contest_id")) if request.user.is_admin() and contest.created_by != request.user: @@ -257,30 +240,18 @@ class ContestProblemAPI(APIView): _id = data["_id"] if not _id: - return self.error("Display id is required for contest problem") + return self.error("Display ID is required") if Problem.objects.filter(_id=_id, contest=contest).exists(): return self.error("Duplicate Display id") - if data["spj"]: - if not data["spj_language"] or not data["spj_code"]: - return self.error("Invalid spj") - data["spj_version"] = hashlib.md5((data["spj_language"] + ":" + data["spj_code"]).encode("utf-8")).hexdigest() - else: - data["spj_language"] = None - data["spj_code"] = None + error_info = self.common_checks(request) + if error_info: + return self.error(error_info) - if data["rule_type"] == ProblemRuleType.OI: - for item in data["test_case_score"]: - if item["score"] <= 0: - return self.error("Invalid score") # todo check filename and score info - - data["created_by"] = request.user data["contest"] = contest tags = data.pop("tags") - data["languages"] = list(data["languages"]) - problem = Problem.objects.create(**data) for item in tags: @@ -291,6 +262,7 @@ class ContestProblemAPI(APIView): problem.tags.add(tag) return self.success(ContestProblemAdminSerializer(problem).data) + @problem_permission_required def get(self, request): problem_id = request.GET.get("id") contest_id = request.GET.get("contest_id") @@ -314,3 +286,53 @@ class ContestProblemAPI(APIView): if keyword: problems = problems.filter(title__contains=keyword) return self.success(self.paginate_data(request, problems, ContestProblemAdminSerializer)) + + @validate_serializer(EditContestProblemSerializer) + @problem_permission_required + def put(self, request): + data = request.data + try: + contest = Contest.objects.get(id=data.pop("contest_id")) + if request.user.is_admin() and contest.created_by != request.user: + return self.error("Contest does not exist") + except Contest.DoesNotExist: + return self.error("Contest does not exist") + + if data["rule_type"] != contest.rule_type: + return self.error("Invalid rule type") + + problem_id = data.pop("id") + user = request.user + + try: + problem = Problem.objects.get(id=problem_id, contest=contest) + if not user.can_mgmt_all_problem() and problem.created_by != user: + return self.error("Problem does not exist") + except Problem.DoesNotExist: + return self.error("Problem does not exist") + + _id = data["_id"] + if not _id: + return self.error("Display ID is required") + if Problem.objects.exclude(id=problem_id).filter(_id=_id, contest=contest).exists(): + return self.error("Display ID already exists") + + error_info = self.common_checks(request) + if error_info: + return self.error(error_info) + # todo check filename and score info + tags = data.pop("tags") + data["languages"] = list(data["languages"]) + + for k, v in data.items(): + setattr(problem, k, v) + problem.save() + + problem.tags.remove(*problem.tags.all()) + for tag in tags: + try: + tag = ProblemTag.objects.get(name=tag) + except ProblemTag.DoesNotExist: + tag = ProblemTag.objects.create(name=tag) + problem.tags.add(tag) + return self.success()