From 0a49d9b1a066d0808d49595216eade828c63b438 Mon Sep 17 00:00:00 2001 From: Alie Langston Date: Fri, 22 Sep 2023 13:35:55 -0400 Subject: [PATCH] feat: emit rejected exam signal --- 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 | 22 ++++++++++++++++++++++ 4 files changed, 72 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..6c3990bf 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_rejected_event, emit_exam_attempt_submitted_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.rejected: + emit_exam_attempt_rejected_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..87b7f941 --- /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_REJECTED +from openedx_events.event_bus import get_producer + + +@receiver(EXAM_ATTEMPT_REJECTED) +def listen_for_exam_attempt_rejected(sender, signal, **kwargs): + """ + Publish EXAM_ATTEMPT_REJECTED signal onto the event bus + """ + get_producer().send( + signal=EXAM_ATTEMPT_REJECTED, + 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..06c7375c 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_REJECTED, EXAM_ATTEMPT_SUBMITTED 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_rejected_event(user, course_key, usage_key): + # .. event_implemented_name: EXAM_ATTEMPT_REJECTED + EXAM_ATTEMPT_REJECTED.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..5f1a9511 100644 --- a/edx_exams/apps/core/tests/test_api.py +++ b/edx_exams/apps/core/tests/test_api.py @@ -325,6 +325,28 @@ 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_REJECTED.send_event') + def test_reject_attempt_event_emitted(self, mock_event_send): + update_attempt_status(self.exam_attempt.id, ExamAttemptStatus.rejected) + 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): """