From 2c5a1e42bf5bc866067edccc9aa30b278c73a9f2 Mon Sep 17 00:00:00 2001 From: zema1 Date: Sun, 15 Oct 2017 18:36:55 +0800 Subject: [PATCH] support share submission --- account/models.py | 4 +++ problem/models.py | 2 +- problem/views/oj.py | 56 ++++++++++++++++++++++++++------------- submission/models.py | 10 ++++--- submission/serializers.py | 9 +++++-- submission/views/oj.py | 39 ++++++++++++++++++++------- 6 files changed, 85 insertions(+), 35 deletions(-) diff --git a/account/models.py b/account/models.py index 0b0e6ca5..6ef7f71f 100644 --- a/account/models.py +++ b/account/models.py @@ -74,6 +74,10 @@ class UserProfile(models.Model): # } # }, # "contest_problems": { + # "1": { + # "status": JudgeStatus.ACCEPTED, + # "_id": "1000" + # } # } # } acm_problems_status = JSONField(default=dict) diff --git a/problem/models.py b/problem/models.py index 72f3fc9d..90537932 100644 --- a/problem/models.py +++ b/problem/models.py @@ -65,7 +65,7 @@ class Problem(models.Model): total_score = models.IntegerField(default=0, blank=True) submission_number = models.BigIntegerField(default=0) accepted_number = models.BigIntegerField(default=0) - # ACM rule_type: {JudgeStatus.ACCEPTED: 3, JudgeStaus.WRONG_ANSWER: 11}, the number means count + # {JudgeStatus.ACCEPTED: 3, JudgeStaus.WRONG_ANSWER: 11}, the number means count statistic_info = JSONField(default=dict) class Meta: diff --git a/problem/views/oj.py b/problem/views/oj.py index 6764249d..321b1bb9 100644 --- a/problem/views/oj.py +++ b/problem/views/oj.py @@ -13,6 +13,25 @@ class ProblemTagAPI(APIView): class ProblemAPI(APIView): + @staticmethod + def _add_problem_status(request, queryset_values): + if request.user.is_authenticated(): + profile = request.user.userprofile + acm_problems_status = profile.acm_problems_status.get("problems", {}) + oi_problems_status = profile.oi_problems_status.get("problems", {}) + # paginate data + results = queryset_values.get("results") + if results: + problems = results + else: + problems = [queryset_values,] + + for problem in problems: + if problem["rule_type"] == ProblemRuleType.ACM: + problem["my_status"] = acm_problems_status.get(str(problem["id"]), {}).get("status") + else: + problem["my_status"] = oi_problems_status.get(str(problem["id"]), {}).get("status") + def get(self, request): # 问题详情页 problem_id = request.GET.get("problem_id") @@ -20,7 +39,9 @@ class ProblemAPI(APIView): try: problem = Problem.objects.select_related("created_by")\ .get(_id=problem_id, contest_id__isnull=True, visible=True) - return self.success(ProblemSerializer(problem).data) + problem_data = ProblemSerializer(problem).data + self._add_problem_status(request, problem_data) + return self.success(problem_data) except Problem.DoesNotExist: return self.error("Problem does not exist") @@ -49,19 +70,21 @@ class ProblemAPI(APIView): problems = problems.filter(difficulty=difficulty) # 根据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(str(problem["id"]), {}).get("status") - else: - problem["my_status"] = oi_problems_status.get(str(problem["id"]), {}).get("status") + self._add_problem_status(request, data) return self.success(data) class ContestProblemAPI(APIView): + def _add_problem_status(self, request, queryset_values): + if request.user.is_authenticated() and self.contest.rule_type != ContestRuleType.OI: + 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 queryset_values: + problem["my_status"] = problems_status.get(str(problem["id"]), {}).get("status") + @check_contest_permission def get(self, request): problem_id = request.GET.get("problem_id") @@ -72,17 +95,12 @@ class ContestProblemAPI(APIView): visible=True) except Problem.DoesNotExist: return self.error("Problem does not exist.") - return self.success(ContestProblemSerializer(problem).data) + problem_data = ContestProblemSerializer(problem).data + self._add_problem_status(request, problem_data) + return self.success(problem_data) contest_problems = Problem.objects.select_related("created_by").filter(contest=self.contest, visible=True) # 根据profile, 为做过的题目添加标记 data = ContestProblemSerializer(contest_problems, many=True).data - if request.user.is_authenticated() and self.contest.rule_type != ContestRuleType.OI: - 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(str(problem["id"]), None) + self._add_problem_status(request, data) return self.success(data) diff --git a/submission/models.py b/submission/models.py index ef408935..7f046a04 100644 --- a/submission/models.py +++ b/submission/models.py @@ -30,7 +30,7 @@ class Submission(models.Model): username = models.CharField(max_length=30) code = models.TextField() result = models.IntegerField(db_index=True, default=JudgeStatus.PENDING) - # 判题结果的详细信息 + # 从JudgeServer返回的判题详情 info = JSONField(default=dict) language = models.CharField(max_length=20) shared = models.BooleanField(default=False) @@ -38,10 +38,12 @@ class Submission(models.Model): # {time_cost: "", memory_cost: "", err_info: "", score: 0} statistic_info = JSONField(default=dict) - def check_user_permission(self, user): + def check_user_permission(self, user, check_share=True): return self.user_id == user.id or \ - self.shared is True or \ - user.admin_type == AdminType.SUPER_ADMIN + (check_share and self.shared is True) or \ + user.is_super_admin() or \ + user.can_mgmt_all_problem() or \ + self.problem.created_by_id == user.id class Meta: db_table = "submission" diff --git a/submission/serializers.py b/submission/serializers.py index ae8c3a60..66a517bd 100644 --- a/submission/serializers.py +++ b/submission/serializers.py @@ -10,6 +10,11 @@ class CreateSubmissionSerializer(serializers.Serializer): contest_id = serializers.IntegerField(required=False) +class ShareSubmissionSerializer(serializers.Serializer): + id = serializers.CharField() + shared = serializers.BooleanField() + + class SubmissionModelSerializer(serializers.ModelSerializer): info = serializers.JSONField() statistic_info = serializers.JSONField() @@ -19,7 +24,7 @@ class SubmissionModelSerializer(serializers.ModelSerializer): # 不显示submission info的serializer, 用于ACM rule_type -class SubmissionSafeSerializer(serializers.ModelSerializer): +class SubmissionSafeModelSerializer(serializers.ModelSerializer): problem = serializers.SlugRelatedField(read_only=True, slug_field="_id") statistic_info = serializers.JSONField() @@ -43,6 +48,6 @@ class SubmissionListSerializer(serializers.ModelSerializer): def get_show_link(self, obj): # 没传user或为匿名user - if self.user is None or self.user.id is None: + if self.user is None or not self.user.is_authenticated(): return False return obj.check_user_permission(self.user) diff --git a/submission/views/oj.py b/submission/views/oj.py index e9672a0a..c4bfe21b 100644 --- a/submission/views/oj.py +++ b/submission/views/oj.py @@ -7,8 +7,9 @@ from utils.api import APIView, validate_serializer from utils.throttling import TokenBucket, BucketController from utils.cache import cache from ..models import Submission -from ..serializers import CreateSubmissionSerializer, SubmissionModelSerializer -from ..serializers import SubmissionSafeSerializer, SubmissionListSerializer +from ..serializers import (CreateSubmissionSerializer, SubmissionModelSerializer, + ShareSubmissionSerializer) +from ..serializers import SubmissionSafeModelSerializer, SubmissionListSerializer def _submit(response, user, problem_id, language, code, contest_id): @@ -63,17 +64,37 @@ class SubmissionAPI(APIView): def get(self, request): submission_id = request.GET.get("id") if not submission_id: - return self.error("Parameter id doesn't exist.") + return self.error("Parameter id doesn't exist") try: submission = Submission.objects.select_related("problem").get(id=submission_id) except Submission.DoesNotExist: - return self.error("Submission doesn't exist.") + return self.error("Submission doesn't exist") if not submission.check_user_permission(request.user): - return self.error("No permission for this submission.") + return self.error("No permission for this submission") if submission.problem.rule_type == ProblemRuleType.ACM: - return self.success(SubmissionSafeSerializer(submission).data) - return self.success(SubmissionModelSerializer(submission).data) + submission_data = SubmissionSafeModelSerializer(submission).data + else: + submission_data = SubmissionModelSerializer(submission).data + # 是否有权限取消共享 + submission_data["can_unshare"] = submission.check_user_permission(request.user, check_share=False) + return self.success(submission_data) + + @validate_serializer(ShareSubmissionSerializer) + @login_required + def put(self, request): + try: + submission = Submission.objects.select_related("problem")\ + .get(id=request.data["id"], contest__isnull=True) + except Submission.DoesNotExist: + return self.error("Submission doesn't exist") + if not submission.check_user_permission(request.user, check_share=False): + return self.error("No permission to share the submission") + if submission.contest and submission.contest.status == ContestStatus.CONTEST_UNDERWAY: + return self.error("Can not share submission during a contest going") + submission.shared = request.data["shared"] + submission.save(update_fields=["shared"]) + return self.success() class SubmissionListAPI(APIView): @@ -83,7 +104,7 @@ class SubmissionListAPI(APIView): if request.GET.get("contest_id"): return self.error("Parameter error") - submissions = Submission.objects.filter(contest_id__isnull=True) + submissions = Submission.objects.filter(contest_id__isnull=True).select_related("problem__created_by") problem_id = request.GET.get("problem_id") myself = request.GET.get("myself") result = request.GET.get("result") @@ -112,7 +133,7 @@ class ContestSubmissionListAPI(APIView): if contest.rule_type == ContestRuleType.OI and not contest.is_contest_admin(request.user): return self.error("No permission for OI contest submissions") - submissions = Submission.objects.filter(contest_id=contest.id) + submissions = Submission.objects.filter(contest_id=contest.id).select_related("problem__created_by") problem_id = request.GET.get("problem_id") myself = request.GET.get("myself") result = request.GET.get("result")