diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index 2801a0a5d1206297fc44e6a21cbad16dfc2c7651..5bf61eb518359b7255d9c1ba5606577231519288 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -1232,6 +1232,7 @@ class TestSubmitPhotosForVerification(MockS3BotoMixin, TestVerificationBase): PASSWORD = "test_password" IMAGE_DATA = "abcd,1234" FULL_NAME = "Ḟüḷḷ Ṅäá¹Ã«" + EXPERIMENT_NAME = "test-experiment" def setUp(self): super().setUp() @@ -1374,14 +1375,37 @@ class TestSubmitPhotosForVerification(MockS3BotoMixin, TestVerificationBase): # Now the request should succeed self._submit_photos(face_image=self.IMAGE_DATA) - # - def _submit_photos(self, face_image=None, photo_id_image=None, full_name=None, expected_status_code=200): + @patch('lms.djangoapps.verify_student.views.segment.track') + def test_experiment_name_param(self, mock_segment_track): + # Submit the photos + self._submit_photos( + face_image=self.IMAGE_DATA, + photo_id_image=self.IMAGE_DATA, + experiment_name=self.EXPERIMENT_NAME + ) + + # Verify that the attempt is created in the database + attempt = SoftwareSecurePhotoVerification.objects.get(user=self.user) + assert attempt.status == 'submitted' + + # assert that segment tracking has been called with experiment name + data = { + "attempt_id": attempt.id, + "experiment_name": self.EXPERIMENT_NAME + } + mock_segment_track.assert_any_call(self.user.id, "edx.bi.experiment.verification.attempt", data) + + def _submit_photos( + self, face_image=None, photo_id_image=None, + full_name=None, experiment_name=None, expected_status_code=200 + ): """Submit photos for verification. Keyword Arguments: face_image (str): The base-64 encoded face image data. photo_id_image (str): The base-64 encoded ID image data. full_name (unicode): The full name of the user, if the user is changing it. + experiment_name (str): Name of A/B experiment associated with attempt expected_status_code (int): The expected response status code. Returns: @@ -1400,6 +1424,9 @@ class TestSubmitPhotosForVerification(MockS3BotoMixin, TestVerificationBase): if full_name is not None: params['full_name'] = full_name + if experiment_name is not None: + params['experiment_name'] = experiment_name + with self.immediate_on_commit(): response = self.client.post(url, params) assert response.status_code == expected_status_code @@ -1561,7 +1588,8 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase, TestVerification ) @patch('lms.djangoapps.verify_student.views.log.error') @patch('sailthru.sailthru_client.SailthruClient.send') - def test_passed_status_template(self, _mock_sailthru_send, _mock_log_error): + @patch('lms.djangoapps.verify_student.views.segment.track') + def test_passed_status_template(self, mock_segment_track, _mock_sailthru_send, _mock_log_error): """ Test for verification passed. """ @@ -1595,13 +1623,21 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase, TestVerification assert response.content.decode('utf-8') == 'OK!' self._assert_verification_approved_email(expiration_datetime.date()) + # assert that segment tracking has been called with result + data = { + "attempt_id": attempt.id, + "result": "PASS" + } + mock_segment_track.assert_called_with(attempt.user.id, "edx.bi.experiment.verification.attempt.result", data) + @patch( 'lms.djangoapps.verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature) ) @patch('lms.djangoapps.verify_student.views.log.error') @patch('sailthru.sailthru_client.SailthruClient.send') - def test_first_time_verification(self, mock_sailthru_send, mock_log_error): # pylint: disable=unused-argument + @patch('lms.djangoapps.verify_student.views.segment.track') + def test_first_time_verification(self, mock_segment_track, mock_sailthru_send, mock_log_error): # pylint: disable=unused-argument """ Test for verification passed if the learner does not have any previous verification """ @@ -1629,13 +1665,21 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase, TestVerification assert response.content.decode('utf-8') == 'OK!' self._assert_verification_approved_email(expiration_datetime.date()) + # assert that segment tracking has been called with result + data = { + "attempt_id": attempt.id, + "result": "PASS" + } + mock_segment_track.assert_called_with(attempt.user.id, "edx.bi.experiment.verification.attempt.result", data) + @patch( 'lms.djangoapps.verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature) ) @patch('lms.djangoapps.verify_student.views.log.error') @patch('sailthru.sailthru_client.SailthruClient.send') - def test_failed_status_template(self, _mock_sailthru_send, _mock_log_error): + @patch('lms.djangoapps.verify_student.views.segment.track') + def test_failed_status_template(self, mock_segment_track, _mock_sailthru_send, _mock_log_error): """ Test for failed verification. """ @@ -1660,11 +1704,19 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase, TestVerification assert response.content.decode('utf-8') == 'OK!' self._assert_verification_denied_email() + # assert that segment tracking has been called with result + data = { + "attempt_id": attempt.id, + "result": "FAIL" + } + mock_segment_track.assert_called_with(attempt.user.id, "edx.bi.experiment.verification.attempt.result", data) + @patch( 'lms.djangoapps.verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature) ) - def test_system_fail_result(self): + @patch('lms.djangoapps.verify_student.views.segment.track') + def test_system_fail_result(self, mock_segment_track): """ Test for software secure result system failure. """ @@ -1686,6 +1738,13 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase, TestVerification assert attempt.error_msg == '"Memory overflow"' assert response.content.decode('utf-8') == 'OK!' + # assert that segment tracking has been called with result + data = { + "attempt_id": attempt.id, + "result": "SYSTEM FAIL" + } + mock_segment_track.assert_called_with(attempt.user.id, "edx.bi.experiment.verification.attempt.result", data) + @patch( 'lms.djangoapps.verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature) diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index eb46811eb89d58e9d80ec49e28ab546ff38c6caf..b8777dea639a9b04ca3a9e45789d5d869b972820 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -833,6 +833,7 @@ class SubmitPhotosView(View): face_image (str): base64-encoded image data of the user's face. photo_id_image (str): base64-encoded image data of the user's photo ID. full_name (str): The user's full name, if the user is requesting a name change as well. + experiment_name (str): The name of an A/B experiment associated with this attempt """ # If the user already has an initial verification attempt, we can re-use the photo ID @@ -865,7 +866,14 @@ class SubmitPhotosView(View): return response # Submit the attempt - self._submit_attempt(request.user, face_image, photo_id_image, initial_verification) + attempt = self._submit_attempt(request.user, face_image, photo_id_image, initial_verification) + + # Send event to segment for analyzing A/B testing data + data = { + "attempt_id": attempt.id, + "experiment_name": params.get("experiment_name", "original") + } + self._fire_event(request.user, "edx.bi.experiment.verification.attempt", data) self._fire_event(request.user, "edx.bi.verify.submitted", {"category": "verification"}) self._send_confirmation_email(request.user) @@ -889,7 +897,8 @@ class SubmitPhotosView(View): for param_name in [ "face_image", "photo_id_image", - "full_name" + "full_name", + "experiment_name" ] if param_name in request.POST } @@ -1150,6 +1159,13 @@ def results_callback(request): # lint-amnesty, pylint: disable=too-many-stateme f"Result {result} not understood. Known results: PASS, FAIL, SYSTEM FAIL" ) + # Send event to segment for analyzing A/B testing data + data = { + "attempt_id": attempt.id, + "result": result + } + segment.track(attempt.user.id, "edx.bi.experiment.verification.attempt.result", data) + return HttpResponse("OK!")