From dbc11a056fa079e8b0a872f4c06863bc12e40833 Mon Sep 17 00:00:00 2001 From: Alie Langston Date: Fri, 29 Sep 2023 08:22:56 -0400 Subject: [PATCH] feat: emit verified event for attempt --- edx_exams/apps/core/api.py | 14 +++++++++++--- edx_exams/apps/core/signals/handlers.py | 19 +++++++++++++++++++ edx_exams/apps/core/signals/signals.py | 21 ++++++++++++++++++++- edx_exams/apps/core/tests/test_api.py | 25 +++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 edx_exams/apps/core/signals/handlers.py diff --git a/edx_exams/apps/core/api.py b/edx_exams/apps/core/api.py index 046529c5..0a2ee0e2 100644 --- a/edx_exams/apps/core/api.py +++ b/edx_exams/apps/core/api.py @@ -18,7 +18,7 @@ ) from edx_exams.apps.core.models import Exam, ExamAttempt from edx_exams.apps.core.statuses import ExamAttemptStatus -from edx_exams.apps.core.signals.signals import emit_exam_attempt_submitted_event +from edx_exams.apps.core.signals.signals import emit_exam_attempt_submitted_event, emit_exam_attempt_verified_event log = logging.getLogger(__name__) @@ -94,17 +94,25 @@ def update_attempt_status(attempt_id, to_status): attempt_obj.start_time = datetime.now(pytz.UTC) attempt_obj.allowed_time_limit_mins = _calculate_allowed_mins(attempt_obj.exam) + course_key = CourseKey.from_string(attempt_obj.exam.course_id) + usage_key = UsageKey.from_string(attempt_obj.exam.content_id) + if to_status == ExamAttemptStatus.submitted: attempt_obj.end_time = datetime.now(pytz.UTC) - course_key = CourseKey.from_string(attempt_obj.exam.course_id) - usage_key = UsageKey.from_string(attempt_obj.exam.content_id) emit_exam_attempt_submitted_event( attempt_obj.user, course_key, usage_key ) + if to_status == ExamAttemptStatus.verified: + emit_exam_attempt_verified_event( + attempt_obj.user, + course_key, + usage_key + ) + attempt_obj.status = to_status attempt_obj.save() diff --git a/edx_exams/apps/core/signals/handlers.py b/edx_exams/apps/core/signals/handlers.py new file mode 100644 index 00000000..782e8ea8 --- /dev/null +++ b/edx_exams/apps/core/signals/handlers.py @@ -0,0 +1,19 @@ +""" signal receivers for the exams app """ + +from django.dispatch import receiver +from openedx_events.content_authoring.signals import EXAM_ATTEMPT_VERIFIED +from openedx_events.event_bus import get_producer + + +@receiver(EXAM_ATTEMPT_VERIFIED) +def listen_for_exam_attempt_verified(sender, signal, **kwargs): + """ + Publish EXAM_ATTEMPT_VERIFIED signal onto the event bus + """ + get_producer().send( + signal=EXAM_ATTEMPT_VERIFIED, + topic='exam-attempt-rejected', + event_key_field='exam_attempt.course_key', + event_data={'exam_attempt': kwargs['exam_attempt']}, + event_metadata=kwargs['metadata'], + ) diff --git a/edx_exams/apps/core/signals/signals.py b/edx_exams/apps/core/signals/signals.py index a6a5a151..00c761f6 100644 --- a/edx_exams/apps/core/signals/signals.py +++ b/edx_exams/apps/core/signals/signals.py @@ -1,5 +1,5 @@ from openedx_events.learning.data import ExamAttemptData, UserData, UserPersonalData -from openedx_events.learning.signals import EXAM_ATTEMPT_SUBMITTED +from openedx_events.learning.signals import EXAM_ATTEMPT_SUBMITTED, EXAM_ATTEMPT_VERIFIED def emit_exam_attempt_submitted_event(user, course_key, usage_key): @@ -19,3 +19,22 @@ def emit_exam_attempt_submitted_event(user, course_key, usage_key): usage_key=usage_key, ) ) + + +def emit_exam_attempt_verified_event(user, course_key, usage_key): + # .. event_implemented_name: EXAM_ATTEMPT_VERIFIED + EXAM_ATTEMPT_VERIFIED.send_event( + exam_attempt=ExamAttemptData( + student_user=UserData( + id=user.id, + is_active=user.is_active, + pii=UserPersonalData( + username=user.username, + email=user.email, + name=user.full_name + ) + ), + course_key=course_key, + usage_key=usage_key, + ) + ) diff --git a/edx_exams/apps/core/tests/test_api.py b/edx_exams/apps/core/tests/test_api.py index 767b298b..595d5e37 100644 --- a/edx_exams/apps/core/tests/test_api.py +++ b/edx_exams/apps/core/tests/test_api.py @@ -325,6 +325,31 @@ def test_submit_attempt_event_emitted(self, mock_event_send): ) mock_event_send.assert_called_with(exam_attempt=expected_data) + @patch('edx_exams.apps.core.signals.signals.EXAM_ATTEMPT_VERIFIED.send_event') + def test_verified_attempt_event_emitted(self, mock_event_send): + """ + Test that when an exam is verified, the EXAM_ATTEMPT_VERIFIED Open edX event is emitted. + """ + update_attempt_status(self.exam_attempt.id, ExamAttemptStatus.verified) + self.assertEqual(mock_event_send.call_count, 1) + + usage_key = UsageKey.from_string(self.exam.content_id) + course_key = CourseKey.from_string(self.exam.course_id) + + expected_data = ExamAttemptData( + student_user=UserData( + id=self.user.id, + is_active=self.user.is_active, + pii=UserPersonalData( + username=self.user.username, + email=self.user.email, + name=self.user.full_name + ) + ), + course_key=course_key, + usage_key=usage_key + ) + mock_event_send.assert_called_with(exam_attempt=expected_data) def test_illegal_start(self): """