Repository: Cloud-CV/EvalAI

Issue:
Update when made public timestamp.
**Current behaviour:** Currently we are not updating `when_made_public` timestamp when a user makes his submission public.
**Expected behaviour:** When a user makes his submission public then update this timestamp.
The timestamp must be in` UTC`.

**Dependency:** Dependent on the PR #1179 


Discussion:
@deshraj I think we should update `when_made_public` field only when `is_public` field in `Submissions` model is `true`. OR should we update it every time when a user toggles the `is_public` field. What do you say?
Yeah, everytime when the participant makes it true, we should update the datetime. Does that make sense? 
yes, it does.

Resolving patch:
diff --git a/apps/jobs/models.py b/apps/jobs/models.py
--- a/apps/jobs/models.py
+++ b/apps/jobs/models.py
@@ -106,11 +106,14 @@ def execution_time(self):
 
     def save(self, *args, **kwargs):
 
-        if hasattr(self, 'status'):
-            if self.status == Submission.RUNNING:
-                self.started_at = timezone.now()
-            if self.status == Submission.FINISHED:
-                self.completed_at = timezone.now()
+        # Only save the completed_at time when the object is created.
+        # Dont update the completed_at field at the time of updating submission meta-data or status.
+        if self._state.adding is True:
+            if hasattr(self, 'status'):
+                if self.status == Submission.RUNNING:
+                    self.started_at = timezone.now()
+                if self.status == Submission.FINISHED:
+                    self.completed_at = timezone.now()
 
         if not self.pk:
             sub_num = Submission.objects.filter(
diff --git a/apps/jobs/serializers.py b/apps/jobs/serializers.py
--- a/apps/jobs/serializers.py
+++ b/apps/jobs/serializers.py
@@ -32,7 +32,7 @@ class Meta:
         fields = ('id', 'participant_team', 'participant_team_name', 'execution_time', 'challenge_phase',
                   'created_by', 'status', 'input_file', 'stdout_file', 'stderr_file', 'submitted_at',
                   'method_name', 'method_description', 'project_url', 'publication_url', 'is_public',
-                  'submission_result_file',)
+                  'submission_result_file', 'when_made_public',)
 
     def get_participant_team_name(self, obj):
         return obj.participant_team.team_name
diff --git a/apps/jobs/views.py b/apps/jobs/views.py
--- a/apps/jobs/views.py
+++ b/apps/jobs/views.py
@@ -161,6 +161,14 @@ def change_submission_data_and_visibility(request, challenge_pk, challenge_phase
         response_data = {'error': 'Submission does not exist'}
         return Response(response_data, status=status.HTTP_403_FORBIDDEN)
 
+    try:
+        is_public = request.data['is_public']
+        if is_public is True:
+            when_made_public = datetime.datetime.now()
+            request.data['when_made_public'] = when_made_public
+    except KeyError:
+        pass
+
     serializer = SubmissionSerializer(submission,
                                       data=request.data,
                                       context={


Test patch:
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
@@ -1749,6 +1749,7 @@ def test_get_all_submissions_when_user_is_participant_of_challenge(self):
                 'submission_result_file': None,
                 "submitted_at": "{0}{1}".format(self.submission.submitted_at.isoformat(), 'Z').replace("+00:00", ""),
                 "is_public": self.submission.is_public,
+                "when_made_public": self.submission.when_made_public,
             }
         ]
         self.challenge5.participant_teams.add(self.participant_team6)
diff --git a/tests/unit/jobs/test_views.py b/tests/unit/jobs/test_views.py
--- a/tests/unit/jobs/test_views.py
+++ b/tests/unit/jobs/test_views.py
@@ -1,3 +1,4 @@
+import datetime
 import os
 import shutil
 
@@ -364,6 +365,7 @@ def test_get_challenge_submissions(self):
                 'submission_result_file': None,
                 "submitted_at": "{0}{1}".format(self.submission.submitted_at.isoformat(), 'Z').replace("+00:00", ""),
                 "is_public": self.submission.is_public,
+                "when_made_public": self.submission.when_made_public,
             }
         ]
         self.challenge.participant_teams.add(self.participant_team)
@@ -495,6 +497,7 @@ def setUp(self):
             project_url="http://testserver/",
             publication_url="http://testserver/",
             is_public=True,
+            when_made_public=datetime.datetime.now()
         )
 
         self.url = reverse_lazy('jobs:change_submission_data_and_visibility',
@@ -631,6 +634,54 @@ def test_change_submission_data_and_visibility_when_submission_exist(self):
                 'submission_result_file': None,
                 "submitted_at": "{0}{1}".format(self.submission.submitted_at.isoformat(), 'Z').replace("+00:00", ""),
                 "is_public": self.submission.is_public,
+                "when_made_public": "{0}{1}".format(self.submission.when_made_public.isoformat(),
+                                                    'Z').replace("+00:00", ""),
+            }
+        self.challenge.participant_teams.add(self.participant_team)
+        response = self.client.patch(self.url, self.data)
+        self.assertEqual(response.data, expected)
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+    def test_change_submission_data_and_visibility_when_is_public_is_true(self):
+        self.url = reverse_lazy('jobs:change_submission_data_and_visibility',
+                                kwargs={'challenge_pk': self.challenge.pk,
+                                        'challenge_phase_pk': self.challenge_phase.pk,
+                                        'submission_pk': self.submission.pk})
+        self.data = {
+            'is_public': True
+        }
+        self.challenge.participant_teams.add(self.participant_team)
+        response = self.client.patch(self.url, self.data)
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+    def test_change_submission_data_and_visibility_when_is_public_is_false(self):
+        self.url = reverse_lazy('jobs:change_submission_data_and_visibility',
+                                kwargs={'challenge_pk': self.challenge.pk,
+                                        'challenge_phase_pk': self.challenge_phase.pk,
+                                        'submission_pk': self.submission.pk})
+        self.data = {
+            'is_public': False
+        }
+        expected = {
+                'id': self.submission.id,
+                'participant_team': self.submission.participant_team.pk,
+                'participant_team_name': self.submission.participant_team.team_name,
+                'execution_time': self.submission.execution_time,
+                'challenge_phase': self.submission.challenge_phase.pk,
+                'created_by': self.submission.created_by.pk,
+                'status': self.submission.status,
+                'input_file': "http://testserver%s" % (self.submission.input_file.url),
+                'method_name': self.submission.method_name,
+                'method_description': self.submission.method_description,
+                'project_url': self.submission.project_url,
+                'publication_url': self.submission.publication_url,
+                'stdout_file': None,
+                'stderr_file': None,
+                'submission_result_file': None,
+                "submitted_at": "{0}{1}".format(self.submission.submitted_at.isoformat(), 'Z').replace("+00:00", ""),
+                "is_public": self.submission.is_public,
+                "when_made_public": "{0}{1}".format(self.submission.when_made_public.isoformat(), 'Z')
+                                    .replace("+00:00", ""),
             }
         self.challenge.participant_teams.add(self.participant_team)
         response = self.client.patch(self.url, self.data)