diff --git a/src/apps/api/tests/test_submissions.py b/src/apps/api/tests/test_submissions.py index f498b1608..895fa142e 100644 --- a/src/apps/api/tests/test_submissions.py +++ b/src/apps/api/tests/test_submissions.py @@ -243,10 +243,16 @@ def test_no_one_can_see_detailed_result_when_visualization_is_false(self): resp = self.client.get(url) assert resp.status_code == 404 - def test_who_can_see_detailed_result_when_visualization_is_true(self): + def test_who_can_see_detailed_result_when_visualization_is_true_and_competition_is_private(self): self.comp.enable_detailed_results = True + self.comp.published = False self.comp.save() - url = reverse('submission-get-detail-result', args=(self.existing_submission.pk,)) + + url = reverse("submission-get-detail-result", args=(self.existing_submission.pk,)) + + # Anonymous user cannot see submission detail result + resp = self.client.get(url) + assert resp.status_code == 403 # Competition creator can see detail result self.client.force_login(self.creator) @@ -263,17 +269,17 @@ def test_who_can_see_detailed_result_when_visualization_is_true(self): resp = self.client.get(url) assert resp.status_code == 200 - # approved user can see submission detail result + # Approved user can see submission detail result self.client.force_login(self.participant) resp = self.client.get(url) assert resp.status_code == 200 - # pending user cannot see submission detail result + # Pending user cannot see submission detail result self.client.force_login(self.pending_participant) resp = self.client.get(url) assert resp.status_code == 403 - # denied user cannot see submission detail result + # Denied user cannot see submission detail result self.client.force_login(self.denied_participant) resp = self.client.get(url) assert resp.status_code == 403 @@ -283,6 +289,45 @@ def test_who_can_see_detailed_result_when_visualization_is_true(self): resp = self.client.get(url) assert resp.status_code == 403 + def test_who_can_see_detailed_result_when_visualization_is_true_and_competition_is_public(self): + self.comp.enable_detailed_results = True + self.comp.published = True + self.comp.save() + + url = reverse("submission-get-detail-result", args=(self.existing_submission.pk,)) + + # Detailed results are publicly available + resp = self.client.get(url) + assert resp.status_code == 200 + + self.client.force_login(self.creator) + resp = self.client.get(url) + assert resp.status_code == 200 + + self.client.force_login(self.collaborator) + resp = self.client.get(url) + assert resp.status_code == 200 + + self.client.force_login(self.superuser) + resp = self.client.get(url) + assert resp.status_code == 200 + + self.client.force_login(self.participant) + resp = self.client.get(url) + assert resp.status_code == 200 + + self.client.force_login(self.pending_participant) + resp = self.client.get(url) + assert resp.status_code == 200 + + self.client.force_login(self.denied_participant) + resp = self.client.get(url) + assert resp.status_code == 200 + + self.client.force_login(self.other_user) + resp = self.client.get(url) + assert resp.status_code == 200 + class SubmissionUpdateTest(APITestCase): def setUp(self): diff --git a/src/apps/api/views/submissions.py b/src/apps/api/views/submissions.py index b91ecf620..753c1ff9f 100644 --- a/src/apps/api/views/submissions.py +++ b/src/apps/api/views/submissions.py @@ -443,37 +443,47 @@ def get_details(self, request, pk): @action(detail=True, methods=('GET',)) def get_detail_result(self, request, pk): - submission = Submission.objects.get(pk=pk) - # Check if competition show visualization is true - if submission.phase.competition.enable_detailed_results: - # get submission's competition approved participants - approved_participants = submission.phase.competition.participants.filter(status=CompetitionParticipant.APPROVED) - participant_usernames = [participant.user.username for participant in approved_participants] - - # check if in this competition - # user is collaborator - # or - # user is approved participant - # or - # user is creator - # or - # user is super user - if request.user in submission.phase.competition.collaborators.all() or\ - request.user.username in participant_usernames or\ - request.user == submission.phase.competition.created_by or\ - request.user.is_superuser: - - data = SubmissionFilesSerializer(submission, context=self.get_serializer_context()).data - return Response(data["detailed_result"], status=status.HTTP_200_OK) + submission = get_object_or_404(Submission, pk=pk) + competition = submission.phase.competition + # Helper to avoid repeating serialization/Response code + def _allowed(): + data = SubmissionFilesSerializer(submission, context=self.get_serializer_context()).data + return Response(data.get("detailed_result"), status=status.HTTP_200_OK) + + # Check if competition show visualization is true + if competition.enable_detailed_results: + if competition.published: + # Detailed results are publicly available + return _allowed() else: - return Response({ - "error_msg": "You do not have permission to see the detailed result. Participate in this competition to view result."}, - status=status.HTTP_403_FORBIDDEN - ) + # Competition is private + user = request.user + if not user.is_authenticated: + return Response( + {"error_msg": "You do not have permission to see the detailed result. Participate in this competition to view result."}, + status=status.HTTP_403_FORBIDDEN, + ) + # Give access if user is collaborator, approved participant, + # competition creator or super user + is_collaborator = competition.collaborators.filter(pk=user.pk).exists() + is_creator = (user == competition.created_by) + is_superuser = user.is_superuser + is_approved_participant = CompetitionParticipant.objects.filter( + competition=competition, + user=user, + status=CompetitionParticipant.APPROVED, + ).exists() + if is_collaborator or is_approved_participant or is_creator or is_superuser: + # Allow access + return _allowed() + return Response( + {"error_msg": "You do not have permission to see the detailed result."}, + status=status.HTTP_403_FORBIDDEN, + ) else: - return Response({ - "error_msg": "Detailed results are disable for this competition!"}, + return Response( + {"error_msg": "Detailed results are disabled for this competition!"}, status=status.HTTP_404_NOT_FOUND ) diff --git a/src/apps/competitions/views.py b/src/apps/competitions/views.py index 6d2aa3c3e..33d4b7a78 100644 --- a/src/apps/competitions/views.py +++ b/src/apps/competitions/views.py @@ -102,5 +102,5 @@ def get_context_data(self, **kwargs): return context -class CompetitionDetailedResults(LoginRequiredMixin, TemplateView): +class CompetitionDetailedResults(TemplateView): template_name = 'competitions/detailed_results.html'