diff --git a/edx_exams/apps/core/api.py b/edx_exams/apps/core/api.py index 046529c5..b6173fef 100644 --- a/edx_exams/apps/core/api.py +++ b/edx_exams/apps/core/api.py @@ -17,8 +17,8 @@ ExamIllegalStatusTransition ) 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.statuses import ExamAttemptStatus log = logging.getLogger(__name__) @@ -102,7 +102,8 @@ def update_attempt_status(attempt_id, to_status): emit_exam_attempt_submitted_event( attempt_obj.user, course_key, - usage_key + usage_key, + attempt_obj.exam.exam_type ) attempt_obj.status = to_status diff --git a/edx_exams/apps/core/apps.py b/edx_exams/apps/core/apps.py new file mode 100644 index 00000000..b73c1398 --- /dev/null +++ b/edx_exams/apps/core/apps.py @@ -0,0 +1,18 @@ +""" +Core Application Configuration +""" + +from django.apps import AppConfig + + +class CoreConfig(AppConfig): + """ + Application configuration for core application. + """ + name = 'edx_exams.apps.core' + + def ready(self): + """ + Connect handlers to signals. + """ + from .signals import handlers # pylint: disable=unused-import,import-outside-toplevel diff --git a/edx_exams/apps/core/signals/__init__.py b/edx_exams/apps/core/signals/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/edx_exams/apps/core/signals/handlers.py b/edx_exams/apps/core/signals/handlers.py new file mode 100644 index 00000000..8e4fdf35 --- /dev/null +++ b/edx_exams/apps/core/signals/handlers.py @@ -0,0 +1,20 @@ +""" +Signal handlers for the edx-exams application. +""" +from django.dispatch import receiver +from openedx_events.event_bus import get_producer +from openedx_events.learning.signals import EXAM_ATTEMPT_SUBMITTED + + +@receiver(EXAM_ATTEMPT_SUBMITTED) +def listen_for_exam_attempt_submitted(sender, signal, **kwargs): # pylint: disable=unused-argument + """ + Publish EXAM_ATTEMPT_SUBMITTED signals onto the event bus. + """ + get_producer().send( + signal=EXAM_ATTEMPT_SUBMITTED, + topic='exam-attempt-submitted', + 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..56141dc2 100644 --- a/edx_exams/apps/core/signals/signals.py +++ b/edx_exams/apps/core/signals/signals.py @@ -1,21 +1,33 @@ +""" +Signal definitions and functions to send those signals for the edx-exams application. +""" + from openedx_events.learning.data import ExamAttemptData, UserData, UserPersonalData from openedx_events.learning.signals import EXAM_ATTEMPT_SUBMITTED -def emit_exam_attempt_submitted_event(user, course_key, usage_key): +def emit_exam_attempt_submitted_event(user, course_key, usage_key, exam_type): + """ + Emit the EXAM_ATTEMPT_SUBMITTED Open edX event. + """ + name = user.full_name or '' + user_data = UserData( + id=user.id, + is_active=user.is_active, + pii=UserPersonalData( + username=user.username, + email=user.email, + name=name + ) + ) + # .. event_implemented_name: EXAM_ATTEMPT_SUBMITTED EXAM_ATTEMPT_SUBMITTED.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 - ) - ), + student_user=user_data, course_key=course_key, usage_key=usage_key, + exam_type=exam_type, + requesting_user=user_data ) ) diff --git a/edx_exams/apps/core/tests/test_api.py b/edx_exams/apps/core/tests/test_api.py index 767b298b..49553449 100644 --- a/edx_exams/apps/core/tests/test_api.py +++ b/edx_exams/apps/core/tests/test_api.py @@ -307,25 +307,27 @@ def test_submit_attempt_event_emitted(self, mock_event_send): update_attempt_status(self.exam_attempt.id, ExamAttemptStatus.submitted) self.assertEqual(mock_event_send.call_count, 1) - usage_key = UsageKey.from_string(self.exam.content_id) + user_data = 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 = CourseKey.from_string(self.exam.course_id) + usage_key = UsageKey.from_string(self.exam.content_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 - ) - ), + student_user=user_data, course_key=course_key, - usage_key=usage_key + usage_key=usage_key, + exam_type=self.exam.exam_type, + requesting_user=user_data, ) mock_event_send.assert_called_with(exam_attempt=expected_data) - def test_illegal_start(self): """ Test that an already started exam cannot be started