Repository: Cloud-CV/EvalAI

Issue:
Export my submissions table as csv for a participant.
Expected output: The challenge participant must export the my-submissions table as a ` csv` based on the challenge phase.


Discussion:

Resolving patch:
diff --git a/apps/challenges/urls.py b/apps/challenges/urls.py
--- a/apps/challenges/urls.py
+++ b/apps/challenges/urls.py
@@ -31,7 +31,8 @@
     url(r'(?P<challenge_pk>[0-9]+)/challenge_phase/(?P<challenge_phase_pk>[0-9]+)/submissions$',
         views.get_all_submissions_of_challenge,
         name='get_all_submissions_of_challenge'),
-    url(r'(?P<challenge_pk>[0-9]+)/download_all_submissions_file/(?P<file_type>[A-Za-z]+)/$',
-        views.download_all_submissions_file,
-        name='download_all_submissions_file')
+    url(r'(?P<challenge_pk>[0-9]+)/phase/(?P<challenge_phase_pk>[0-9]+)'
+        r'/download_all_submissions/(?P<file_type>[A-Za-z]+)/$',
+        views.download_all_submissions,
+        name='download_all_submissions')
 ]
diff --git a/apps/challenges/views.py b/apps/challenges/views.py
--- a/apps/challenges/views.py
+++ b/apps/challenges/views.py
@@ -744,11 +744,18 @@ def get_all_submissions_of_challenge(request, challenge_pk, challenge_phase_pk):
 @api_view(['GET'])
 @permission_classes((permissions.IsAuthenticated, HasVerifiedEmail))
 @authentication_classes((ExpiringTokenAuthentication,))
-def download_all_submissions_file(request, challenge_pk, file_type):
+def download_all_submissions(request, challenge_pk, challenge_phase_pk, file_type):
 
     # To check for the corresponding challenge from challenge_pk.
     challenge = get_challenge_model(challenge_pk)
 
+    # To check for the corresponding challenge phase from the challenge_phase_pk and challenge.
+    try:
+        challenge_phase = ChallengePhase.objects.get(pk=challenge_phase_pk, challenge=challenge)
+    except ChallengePhase.DoesNotExist:
+        response_data = {'error': 'Challenge Phase {} does not exist'.format(challenge_phase_pk)}
+        return Response(response_data, status=status.HTTP_404_NOT_FOUND)
+
     if file_type == 'csv':
         if is_user_a_host_of_challenge(user=request.user, challenge_pk=challenge_pk):
             submissions = Submission.objects.filter(challenge_phase__challenge=challenge).order_by('-submitted_at')
@@ -785,8 +792,44 @@ def download_all_submissions_file(request, challenge_pk, file_type):
                                  submission.submission_metadata_file,
                                  ])
             return response
+
+        elif has_user_participated_in_challenge(user=request.user, challenge_id=challenge_pk):
+
+            # get participant team object for the user for a particular challenge.
+            participant_team_pk = get_participant_team_id_of_user_for_a_challenge(
+                request.user, challenge_pk)
+
+            # Filter submissions on the basis of challenge phase for a participant.
+            submissions = Submission.objects.filter(participant_team=participant_team_pk,
+                                                    challenge_phase=challenge_phase).order_by('-submitted_at')
+
+            response = HttpResponse(content_type='text/csv')
+            response['Content-Disposition'] = 'attachment; filename=all_submissions.csv'
+            writer = csv.writer(response)
+            writer.writerow(['Team Name',
+                             'Method Name',
+                             'Status',
+                             'Execution Time(sec.)',
+                             'Submitted File',
+                             'Result File',
+                             'Stdout File',
+                             'Stderr File',
+                             'Submitted At',
+                             ])
+            for submission in submissions:
+                writer.writerow([submission.participant_team,
+                                 submission.method_name,
+                                 submission.status,
+                                 submission.execution_time,
+                                 submission.input_file,
+                                 submission.submission_result_file,
+                                 submission.stdout_file,
+                                 submission.stderr_file,
+                                 submission.created_at,
+                                 ])
+            return response
         else:
-            response_data = {'error': 'Only Challenge Hosts can use the export feature!'}
+            response_data = {'error': 'You are neither host nor participant of the challenge!'}
             return Response(response_data, status=status.HTTP_400_BAD_REQUEST)
     else:
         response_data = {'error': 'The file type requested is not valid!'}


Test patch:
diff --git a/tests/unit/challenges/test_urls.py b/tests/unit/challenges/test_urls.py
--- a/tests/unit/challenges/test_urls.py
+++ b/tests/unit/challenges/test_urls.py
@@ -95,10 +95,12 @@ def test_challenges_urls(self):
         url = reverse_lazy('challenges:get_all_challenges', kwargs={'challenge_time': "PAST"})
         self.assertEqual(url, '/api/challenges/challenge/PAST')
 
-        self.url = reverse_lazy('challenges:download_all_submissions_file',
+        self.url = reverse_lazy('challenges:download_all_submissions',
                                 kwargs={'challenge_pk': self.challenge.pk,
+                                        'challenge_phase_pk': self.challenge_phase.pk,
                                         'file_type': self.file_type})
-        self.assertEqual(self.url, '/api/challenges/{}/download_all_submissions_file/{}/'.format(self.challenge.pk,
-                                                                                                 self.file_type))
+        self.assertEqual(self.url,
+                         '/api/challenges/{}/phase/{}/download_all_submissions/{}/'
+                         .format(self.challenge.pk, self.challenge_phase.pk, self.file_type))
         resolver = resolve(self.url)
-        self.assertEqual(resolver.view_name, 'challenges:download_all_submissions_file')
+        self.assertEqual(resolver.view_name, 'challenges:download_all_submissions')
diff --git a/tests/unit/challenges/test_views.py b/tests/unit/challenges/test_views.py
--- a/tests/unit/challenges/test_views.py
+++ b/tests/unit/challenges/test_views.py
@@ -1757,63 +1757,36 @@ class DownloadAllSubmissionsFileTest(BaseAPITestClass):
     def setUp(self):
         super(DownloadAllSubmissionsFileTest, self).setUp()
 
-        self.user8 = User.objects.create(
-            username='otheruser8',
+        self.user1 = User.objects.create(
+            username='otheruser1',
             password='other_secret_password',
-            email='user8@test.com',)
+            email='user1@test.com',)
 
-        self.user9 = User.objects.create(
-            username='otheruser9',
+        self.user2 = User.objects.create(
+            username='otheruser2',
             password='other_secret_password',
-            email='user9@test.com',)
+            email='user2@test.com',)
 
         EmailAddress.objects.create(
-            user=self.user8,
-            email='user8@test.com',
+            user=self.user1,
+            email='user1@test.com',
             primary=True,
             verified=True)
 
         EmailAddress.objects.create(
-            user=self.user9,
-            email='user9@test.com',
+            user=self.user2,
+            email='user2@test.com',
             primary=True,
             verified=True)
 
-        self.challenge_host_team8 = ChallengeHostTeam.objects.create(
-            team_name='Other Test Challenge Host Team',
-            created_by=self.user8
-        )
-
-        # Now allot self.user as also a host of self.challenge_host_team1
-        self.challenge_host8 = ChallengeHost.objects.create(
-            user=self.user8,
-            team_name=self.challenge_host_team8,
-            status=ChallengeHost.ACCEPTED,
-            permissions=ChallengeHost.ADMIN
-        )
-
-        self.participant_team8 = ParticipantTeam.objects.create(
+        self.participant_team1 = ParticipantTeam.objects.create(
             team_name='Participant Team for Challenge8',
-            created_by=self.user8)
+            created_by=self.user1)
 
-        self.participant8 = Participant.objects.create(
-            user=self.user8,
+        self.participant1 = Participant.objects.create(
+            user=self.user1,
             status=Participant.ACCEPTED,
-            team=self.participant_team)
-
-        self.challenge8 = Challenge.objects.create(
-            title='Other Test Challenge',
-            short_description='Short description for other test challenge',
-            description='Description for other test challenge',
-            terms_and_conditions='Terms and conditions for other test challenge',
-            submission_guidelines='Submission guidelines for other test challenge',
-            creator=self.challenge_host_team8,
-            published=False,
-            enable_forum=True,
-            anonymous_leaderboard=False,
-            start_date=timezone.now() - timedelta(days=2),
-            end_date=timezone.now() + timedelta(days=1),
-        )
+            team=self.participant_team1)
 
         try:
             os.makedirs('/tmp/evalai')
@@ -1821,22 +1794,22 @@ def setUp(self):
             pass
 
         with self.settings(MEDIA_ROOT='/tmp/evalai'):
-            self.challenge_phase8 = ChallengePhase.objects.create(
+            self.challenge_phase = ChallengePhase.objects.create(
                 name='Challenge Phase',
                 description='Description for Challenge Phase',
                 leaderboard_public=False,
                 is_public=True,
                 start_date=timezone.now() - timedelta(days=2),
                 end_date=timezone.now() + timedelta(days=1),
-                challenge=self.challenge8,
+                challenge=self.challenge,
                 test_annotation=SimpleUploadedFile('test_sample_file.txt',
                                                    'Dummy file content', content_type='text/plain')
             )
         with self.settings(MEDIA_ROOT='/tmp/evalai'):
-            self.submission8 = Submission.objects.create(
-                participant_team=self.participant_team8,
-                challenge_phase=self.challenge_phase8,
-                created_by=self.challenge_host_team8.created_by,
+            self.submission = Submission.objects.create(
+                participant_team=self.participant_team1,
+                challenge_phase=self.challenge_phase,
+                created_by=self.participant_team1.created_by,
                 status='submitted',
                 input_file=SimpleUploadedFile('test_sample_file.txt',
                                               'Dummy file content', content_type='text/plain'),
@@ -1851,22 +1824,34 @@ def setUp(self):
 
         self.file_type_pdf = 'pdf'
 
-        self.client.force_authenticate(user=self.user8)
+    def test_download_all_submissions_when_challenge_does_not_exist(self):
+        self.url = reverse_lazy('challenges:download_all_submissions',
+                                kwargs={'challenge_pk': self.challenge.pk+10,
+                                        'challenge_phase_pk': self.challenge_phase.pk,
+                                        'file_type': self.file_type_csv})
+        expected = {
+            'detail': 'Challenge {} does not exist'.format(self.challenge.pk+10)
+        }
+        response = self.client.get(self.url, {})
+        self.assertEqual(response.data, expected)
+        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
 
-    def test_download_all_submissions_file_when_challenge_does_not_exist(self):
-        self.url = reverse_lazy('challenges:download_all_submissions_file',
-                                kwargs={'challenge_pk': self.challenge8.pk+10,
+    def test_download_all_submissions_when_challenge_phase_does_not_exist(self):
+        self.url = reverse_lazy('challenges:download_all_submissions',
+                                kwargs={'challenge_pk': self.challenge.pk,
+                                        'challenge_phase_pk': self.challenge_phase.pk+10,
                                         'file_type': self.file_type_csv})
         expected = {
-            'detail': 'Challenge {} does not exist'.format(self.challenge8.pk+10)
+            'error': 'Challenge Phase {} does not exist'.format(self.challenge_phase.pk+10)
         }
         response = self.client.get(self.url, {})
         self.assertEqual(response.data, expected)
         self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
 
     def test_download_all_submissions_when_file_type_is_not_csv(self):
-        self.url = reverse_lazy('challenges:download_all_submissions_file',
-                                kwargs={'challenge_pk': self.challenge8.pk,
+        self.url = reverse_lazy('challenges:download_all_submissions',
+                                kwargs={'challenge_pk': self.challenge.pk,
+                                        'challenge_phase_pk': self.challenge_phase.pk,
                                         'file_type': self.file_type_pdf})
         expected = {
             'error': 'The file type requested is not valid!'
@@ -1875,22 +1860,35 @@ def test_download_all_submissions_when_file_type_is_not_csv(self):
         self.assertEqual(response.data, expected)
         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
 
-    def test_download_all_submissions(self):
-        self.url = reverse_lazy('challenges:download_all_submissions_file',
-                                kwargs={'challenge_pk': self.challenge8.pk,
+    def test_download_all_submissions_when_user_is_challenge_host(self):
+        self.url = reverse_lazy('challenges:download_all_submissions',
+                                kwargs={'challenge_pk': self.challenge.pk,
+                                        'challenge_phase_pk': self.challenge_phase.pk,
                                         'file_type': self.file_type_csv})
         response = self.client.get(self.url, {})
         self.assertEqual(response.status_code, status.HTTP_200_OK)
 
-    def test_download_all_submissions_when_user_is_not_a_challenge_host(self):
-        self.url = reverse_lazy('challenges:download_all_submissions_file',
-                                kwargs={'challenge_pk': self.challenge8.pk,
+    def test_download_all_submissions_when_user_is_challenge_participant(self):
+        self.url = reverse_lazy('challenges:download_all_submissions',
+                                kwargs={'challenge_pk': self.challenge.pk,
+                                        'challenge_phase_pk': self.challenge_phase.pk,
                                         'file_type': self.file_type_csv})
 
-        self.client.force_authenticate(user=self.user9)
+        self.challenge.participant_teams.add(self.participant_team1)
+        self.challenge.save()
+        response = self.client.get(self.url, {})
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+    def test_download_all_submissions_when_user_is_neither_a_challenge_host_nor_a_participant(self):
+        self.url = reverse_lazy('challenges:download_all_submissions',
+                                kwargs={'challenge_pk': self.challenge.pk,
+                                        'challenge_phase_pk': self.challenge_phase.pk,
+                                        'file_type': self.file_type_csv})
+
+        self.client.force_authenticate(user=self.user2)
 
         expected = {
-            'error': 'Only Challenge Hosts can use the export feature!'
+            'error': 'You are neither host nor participant of the challenge!'
         }
         response = self.client.get(self.url, {})
         self.assertEqual(response.data, expected)