From 523d20e664cb77d9f6afa69ff35395e3c7d5804f Mon Sep 17 00:00:00 2001 From: ilee2u Date: Tue, 10 Sep 2024 16:40:50 -0400 Subject: [PATCH 01/13] feat: add idv events to api - moved what was in signals.py to a handlers.py (which is what their file should have been called) --- lms/djangoapps/verify_student/api.py | 41 +++++ lms/djangoapps/verify_student/handlers.py | 82 ++++++++++ lms/djangoapps/verify_student/signals.py | 146 +++++++++++------- .../verify_student/tests/test_api.py | 5 + 4 files changed, 215 insertions(+), 59 deletions(-) create mode 100644 lms/djangoapps/verify_student/handlers.py diff --git a/lms/djangoapps/verify_student/api.py b/lms/djangoapps/verify_student/api.py index f61b90d682ff..98f8432a375e 100644 --- a/lms/djangoapps/verify_student/api.py +++ b/lms/djangoapps/verify_student/api.py @@ -13,6 +13,12 @@ from lms.djangoapps.verify_student.emails import send_verification_approved_email from lms.djangoapps.verify_student.exceptions import VerificationAttemptInvalidStatus from lms.djangoapps.verify_student.models import VerificationAttempt +from lms.djangoapps.verify_student.signals import ( + emit_idv_attempt_approved_event, + emit_idv_attempt_created_event, + emit_idv_attempt_denied_event, + emit_idv_attempt_pending_event, +) from lms.djangoapps.verify_student.statuses import VerificationAttemptStatus from lms.djangoapps.verify_student.tasks import send_verification_status_email @@ -70,11 +76,20 @@ def create_verification_attempt(user: User, name: str, status: str, expiration_d expiration_datetime=expiration_datetime, ) + emit_idv_attempt_created_event( + attempt_id=verification_attempt.id, + user=user, + status=status, + name=name, + expiration_date=expiration_datetime, + ) + return verification_attempt.id def update_verification_attempt( attempt_id: int, + user: User, name: Optional[str] = None, status: Optional[str] = None, expiration_datetime: Optional[datetime] = None @@ -86,6 +101,7 @@ def update_verification_attempt( Arguments: * attempt_id (int): the verification attempt id of the attempt to update + * user (User): the user (usually a learner) performing the verification attempt * name (string, optional): the new name being ID verified * status (string, optional): the new status of the verification attempt * expiration_datetime (datetime, optional): The new expiration date and time @@ -120,6 +136,31 @@ def update_verification_attempt( ) raise VerificationAttemptInvalidStatus + if status == VerificationAttemptStatus.PENDING: + emit_idv_attempt_pending_event( + attempt_id=attempt_id, + user=user, + status=status, + name=name, + expiration_date=expiration_datetime, + ) + elif status == VerificationAttemptStatus.APPROVED: + emit_idv_attempt_approved_event( + attempt_id=attempt_id, + user=user, + status=status, + name=name, + expiration_date=expiration_datetime, + ) + elif status == VerificationAttemptStatus.DENIED: + emit_idv_attempt_denied_event( + attempt_id=attempt_id, + user=user, + status=status, + name=name, + expiration_date=expiration_datetime, + ) + # NOTE: Generally, we only set the expiration date from the time that an IDV attempt is marked approved, # so we allow expiration_datetime to = None for other status updates (e.g. pending). attempt.expiration_datetime = expiration_datetime diff --git a/lms/djangoapps/verify_student/handlers.py b/lms/djangoapps/verify_student/handlers.py new file mode 100644 index 000000000000..e08f107c44db --- /dev/null +++ b/lms/djangoapps/verify_student/handlers.py @@ -0,0 +1,82 @@ +""" +Signal handler for setting default course verification dates +""" +import logging + +from django.core.exceptions import ObjectDoesNotExist +from django.db.models.signals import post_save +from django.dispatch import Signal +from django.dispatch.dispatcher import receiver +from xmodule.modulestore.django import SignalHandler, modulestore + +from common.djangoapps.student.models_api import get_name, get_pending_name_change +from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC + +from .models import SoftwareSecurePhotoVerification, VerificationDeadline, VerificationAttempt + +log = logging.getLogger(__name__) + +# Signal for emitting IDV submission and review updates +# providing_args = ["attempt_id", "user_id", "status", "full_name", "profile_name"] +idv_update_signal = Signal() + + +@receiver(SignalHandler.course_published) +def _listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable=unused-argument + """ + Catches the signal that a course has been published in Studio and + sets the verification deadline date to a default. + """ + course = modulestore().get_course(course_key) + if course: + try: + deadline = VerificationDeadline.objects.get(course_key=course_key) + if not deadline.deadline_is_explicit and deadline.deadline != course.end: + VerificationDeadline.set_deadline(course_key, course.end) + except ObjectDoesNotExist: + VerificationDeadline.set_deadline(course_key, course.end) + + +@receiver(USER_RETIRE_LMS_CRITICAL) +def _listen_for_lms_retire(sender, **kwargs): # pylint: disable=unused-argument + user = kwargs.get('user') + SoftwareSecurePhotoVerification.retire_user(user.id) + + +@receiver(post_save, sender=SoftwareSecurePhotoVerification) +def send_idv_update(sender, instance, **kwargs): # pylint: disable=unused-argument + """ + Catches the post save signal from the SoftwareSecurePhotoVerification model, and emits + another signal with limited information from the model. We are choosing to re-emit a signal + as opposed to relying only on the post_save signal to avoid the chance that other apps + import the SoftwareSecurePhotoVerification model. + """ + # Prioritize pending name change over current profile name, if the user has one + pending_name_change = get_pending_name_change(instance.user) + if pending_name_change: + full_name = pending_name_change.new_name + else: + full_name = get_name(instance.user.id) + + log.info( + 'IDV sending name_affirmation task (idv_id={idv_id}, user_id={user_id}) to update status={status}'.format( + user_id=instance.user.id, + status=instance.status, + idv_id=instance.id + ) + ) + + idv_update_signal.send( + sender='idv_update', + attempt_id=instance.id, + user_id=instance.user.id, + status=instance.status, + photo_id_name=instance.name, + full_name=full_name + ) + + +@receiver(USER_RETIRE_LMS_MISC) +def _listen_for_lms_retire_verification_attempts(sender, **kwargs): # pylint: disable=unused-argument + user = kwargs.get('user') + VerificationAttempt.retire_user(user.id) diff --git a/lms/djangoapps/verify_student/signals.py b/lms/djangoapps/verify_student/signals.py index ae54deb74214..f9082f7621d5 100644 --- a/lms/djangoapps/verify_student/signals.py +++ b/lms/djangoapps/verify_student/signals.py @@ -1,83 +1,111 @@ """ -Signal handler for setting default course verification dates +Signal definitions and functions to send those signals for the verify_student application. """ -import logging -from django.core.exceptions import ObjectDoesNotExist -from django.db.models.signals import post_save from django.dispatch import Signal -from django.dispatch.dispatcher import receiver -from xmodule.modulestore.django import SignalHandler, modulestore - -from common.djangoapps.student.models_api import get_name, get_pending_name_change -from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC - -from .models import SoftwareSecurePhotoVerification, VerificationDeadline, VerificationAttempt - -log = logging.getLogger(__name__) +from openedx_events.learning.data import UserData, UserPersonalData, VerificationAttemptData +from openedx_events.learning.signals import ( + IDV_ATTEMPT_CREATED, + IDV_ATTEMPT_PENDING, + IDV_ATTEMPT_APPROVED, + IDV_ATTEMPT_DENIED, +) # Signal for emitting IDV submission and review updates # providing_args = ["attempt_id", "user_id", "status", "full_name", "profile_name"] idv_update_signal = Signal() - -@receiver(SignalHandler.course_published) -def _listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable=unused-argument +def _create_user_data(user): """ - Catches the signal that a course has been published in Studio and - sets the verification deadline date to a default. + Helper function to create a UserData object. """ - course = modulestore().get_course(course_key) - if course: - try: - deadline = VerificationDeadline.objects.get(course_key=course_key) - if not deadline.deadline_is_explicit and deadline.deadline != course.end: - VerificationDeadline.set_deadline(course_key, course.end) - except ObjectDoesNotExist: - VerificationDeadline.set_deadline(course_key, course.end) + user_data = UserData( + # NOTE to self: the id field was previously = lms_user_id in edx-exams, which I copied this from + # Do we want that specific ID, or is this fine? + # (There was also a "full_name" field, but I don't think we need that) + id=user.id, + is_active=user.is_active, + pii=UserPersonalData( + username=user.username, + email=user.email, + name=user.get_full_name() + ) + ) + return user_data -@receiver(USER_RETIRE_LMS_CRITICAL) -def _listen_for_lms_retire(sender, **kwargs): # pylint: disable=unused-argument - user = kwargs.get('user') - SoftwareSecurePhotoVerification.retire_user(user.id) +def emit_idv_attempt_created_event(attempt_id, user, status, name, expiration_date): + """ + Emit the IDV_ATTEMPT_CREATED Open edX event. + """ + user_data = _create_user_data(user) + + # .. event_implemented_name: IDV_ATTEMPT_CREATED + IDV_ATTEMPT_CREATED.send_event( + idv_attempt=VerificationAttemptData( + attempt_id=attempt_id, + user=user_data, + status=status, + name=name, + expiration_date=expiration_date, + ) + ) + return user_data -@receiver(post_save, sender=SoftwareSecurePhotoVerification) -def send_idv_update(sender, instance, **kwargs): # pylint: disable=unused-argument + +def emit_idv_attempt_pending_event(attempt_id, user, status, name, expiration_date): """ - Catches the post save signal from the SoftwareSecurePhotoVerification model, and emits - another signal with limited information from the model. We are choosing to re-emit a signal - as opposed to relying only on the post_save signal to avoid the chance that other apps - import the SoftwareSecurePhotoVerification model. + Emit the IDV_ATTEMPT_PENDING Open edX event. """ - # Prioritize pending name change over current profile name, if the user has one - pending_name_change = get_pending_name_change(instance.user) - if pending_name_change: - full_name = pending_name_change.new_name - else: - full_name = get_name(instance.user.id) - - log.info( - 'IDV sending name_affirmation task (idv_id={idv_id}, user_id={user_id}) to update status={status}'.format( - user_id=instance.user.id, - status=instance.status, - idv_id=instance.id + user_data = _create_user_data(user) + + # .. event_implemented_name: IDV_ATTEMPT_PENDING + IDV_ATTEMPT_PENDING.send_event( + idv_attempt=VerificationAttemptData( + attempt_id=attempt_id, + user=user_data, + status=status, + name=name, + expiration_date=expiration_date, ) ) + return user_data + - idv_update_signal.send( - sender='idv_update', - attempt_id=instance.id, - user_id=instance.user.id, - status=instance.status, - photo_id_name=instance.name, - full_name=full_name +def emit_idv_attempt_approved_event(attempt_id, user, status, name, expiration_date): + """ + Emit the IDV_ATTEMPT_APPROVED Open edX event. + """ + user_data = _create_user_data(user) + + # .. event_implemented_name: IDV_ATTEMPT_APPROVED + IDV_ATTEMPT_APPROVED.send_event( + idv_attempt=VerificationAttemptData( + attempt_id=attempt_id, + user=user_data, + status=status, + name=name, + expiration_date=expiration_date, + ) ) + return user_data -@receiver(USER_RETIRE_LMS_MISC) -def _listen_for_lms_retire_verification_attempts(sender, **kwargs): # pylint: disable=unused-argument - user = kwargs.get('user') - VerificationAttempt.retire_user(user.id) +def emit_idv_attempt_denied_event(attempt_id, user, status, name, expiration_date): + """ + Emit the IDV_ATTEMPT_DENIED Open edX event. + """ + user_data = _create_user_data(user) + + # .. event_implemented_name: IDV_ATTEMPT_DENIED + IDV_ATTEMPT_DENIED.send_event( + idv_attempt=VerificationAttemptData( + attempt_id=attempt_id, + user=user_data, + status=status, + name=name, + expiration_date=expiration_date, + ) + ) diff --git a/lms/djangoapps/verify_student/tests/test_api.py b/lms/djangoapps/verify_student/tests/test_api.py index 747c76f82b61..05ba5493a511 100644 --- a/lms/djangoapps/verify_student/tests/test_api.py +++ b/lms/djangoapps/verify_student/tests/test_api.py @@ -133,6 +133,7 @@ def test_update_verification_attempt(self, name, status, expiration_datetime): update_verification_attempt( attempt_id=self.attempt.id, name=name, + user=self.user, status=status, expiration_datetime=expiration_datetime, ) @@ -149,6 +150,7 @@ def test_update_verification_attempt_none_values(self): update_verification_attempt( attempt_id=self.attempt.id, name=None, + user=self.user, status=None, expiration_datetime=None, ) @@ -166,6 +168,8 @@ def test_update_verification_attempt_not_found(self): VerificationAttempt.DoesNotExist, update_verification_attempt, attempt_id=999999, + name=None, + user=self.user, status=VerificationAttemptStatus.APPROVED, ) @@ -181,6 +185,7 @@ def test_update_verification_attempt_invalid(self, status): update_verification_attempt, attempt_id=self.attempt.id, name=None, + user=self.user, status=status, expiration_datetime=None, ) From 5c0a9e56ae0a966cdedcd0d580227e2b284d1648 Mon Sep 17 00:00:00 2001 From: ilee2u Date: Wed, 11 Sep 2024 12:37:06 -0400 Subject: [PATCH 02/13] chore: quality --- lms/djangoapps/verify_student/signals.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lms/djangoapps/verify_student/signals.py b/lms/djangoapps/verify_student/signals.py index f9082f7621d5..a81b26b323ad 100644 --- a/lms/djangoapps/verify_student/signals.py +++ b/lms/djangoapps/verify_student/signals.py @@ -16,6 +16,7 @@ # providing_args = ["attempt_id", "user_id", "status", "full_name", "profile_name"] idv_update_signal = Signal() + def _create_user_data(user): """ Helper function to create a UserData object. From 4ce41737d38d7899141613d0f7d3775f3d7c08a4 Mon Sep 17 00:00:00 2001 From: ilee2u Date: Wed, 11 Sep 2024 13:03:00 -0400 Subject: [PATCH 03/13] fix: rename test file + imports --- .../{test_signals.py => test_handlers.py} | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) rename lms/djangoapps/verify_student/tests/{test_signals.py => test_handlers.py} (88%) diff --git a/lms/djangoapps/verify_student/tests/test_signals.py b/lms/djangoapps/verify_student/tests/test_handlers.py similarity index 88% rename from lms/djangoapps/verify_student/tests/test_signals.py rename to lms/djangoapps/verify_student/tests/test_handlers.py index 8d607988d4b4..34d0a6ccfc21 100644 --- a/lms/djangoapps/verify_student/tests/test_signals.py +++ b/lms/djangoapps/verify_student/tests/test_handlers.py @@ -15,7 +15,7 @@ VerificationDeadline, VerificationAttempt ) -from lms.djangoapps.verify_student.signals import ( +from lms.djangoapps.verify_student.handlers import ( _listen_for_course_publish, _listen_for_lms_retire, _listen_for_lms_retire_verification_attempts @@ -29,9 +29,9 @@ from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order -class VerificationDeadlineSignalTest(ModuleStoreTestCase): +class VerificationDeadlineHandlerTest(ModuleStoreTestCase): """ - Tests for the VerificationDeadline signal + Tests for the VerificationDeadline handler """ def setUp(self): @@ -41,13 +41,13 @@ def setUp(self): VerificationDeadline.objects.all().delete() def test_no_deadline(self): - """ Verify the signal sets deadline to course end when no deadline exists.""" + """ Verify the handler sets deadline to course end when no deadline exists.""" _listen_for_course_publish('store', self.course.id) assert VerificationDeadline.deadline_for_course(self.course.id) == self.course.end def test_deadline(self): - """ Verify deadline is set to course end date by signal when changed. """ + """ Verify deadline is set to course end date by handler when changed. """ deadline = now() - timedelta(days=7) VerificationDeadline.set_deadline(self.course.id, deadline) @@ -55,7 +55,7 @@ def test_deadline(self): assert VerificationDeadline.deadline_for_course(self.course.id) == self.course.end def test_deadline_explicit(self): - """ Verify deadline is unchanged by signal when explicitly set. """ + """ Verify deadline is unchanged by handler when explicitly set. """ deadline = now() - timedelta(days=7) VerificationDeadline.set_deadline(self.course.id, deadline, is_explicit=True) @@ -66,9 +66,9 @@ def test_deadline_explicit(self): assert actual_deadline == deadline -class RetirementSignalTest(ModuleStoreTestCase): +class RetirementHandlerTest(ModuleStoreTestCase): """ - Tests for the VerificationDeadline signal + Tests for the VerificationDeadline handler """ def _create_entry(self): @@ -119,8 +119,8 @@ def test_idempotent(self): class PostSavePhotoVerificationTest(ModuleStoreTestCase): """ - Tests for the post_save signal on the SoftwareSecurePhotoVerification model. - This receiver should emit another signal that contains limited data about + Tests for the post_save handler on the SoftwareSecurePhotoVerification model. + This receiver should emit another handler that contains limited data about the verification attempt that was updated. """ @@ -132,7 +132,7 @@ def setUp(self): self.photo_id_image_url = 'https://test.photo' self.photo_id_key = 'test+key' - @patch('lms.djangoapps.verify_student.signals.idv_update_signal.send') + @patch('lms.djangoapps.verify_student.handlers.idv_update_signal.send') def test_post_save_signal(self, mock_signal): # create new softwaresecureverification attempt = SoftwareSecurePhotoVerification.objects.create( @@ -165,7 +165,7 @@ def test_post_save_signal(self, mock_signal): full_name=attempt.user.profile.name ) - @patch('lms.djangoapps.verify_student.signals.idv_update_signal.send') + @patch('lms.djangoapps.verify_student.handlers.idv_update_signal.send') def test_post_save_signal_pending_name(self, mock_signal): pending_name_change = do_name_change_request(self.user, 'Pending Name', 'test')[0] @@ -187,7 +187,7 @@ def test_post_save_signal_pending_name(self, mock_signal): ) -class RetirementSignalVerificationAttemptsTest(ModuleStoreTestCase): +class RetirementHandlerVerificationAttemptsTest(ModuleStoreTestCase): """ Tests for the LMS User Retirement signal for Verification Attempts """ From 57ca900b1595ed063eb27fab567b5c1545b1e50e Mon Sep 17 00:00:00 2001 From: ilee2u Date: Wed, 11 Sep 2024 13:29:54 -0400 Subject: [PATCH 04/13] fix: change handler reverse url in other tests --- .../commands/tests/test_retry_failed_photo_verifications.py | 2 +- ...trigger_softwaresecurephotoverifications_post_save_signal.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/verify_student/management/commands/tests/test_retry_failed_photo_verifications.py b/lms/djangoapps/verify_student/management/commands/tests/test_retry_failed_photo_verifications.py index 1c3f22aa30cd..cc1739f63fbe 100644 --- a/lms/djangoapps/verify_student/management/commands/tests/test_retry_failed_photo_verifications.py +++ b/lms/djangoapps/verify_student/management/commands/tests/test_retry_failed_photo_verifications.py @@ -121,7 +121,7 @@ def _create_attempts(self, num_attempts): for _ in range(num_attempts): self.create_upload_and_submit_attempt_for_user() - @patch('lms.djangoapps.verify_student.signals.idv_update_signal.send') + @patch('lms.djangoapps.verify_student.handlers.idv_update_signal.send') def test_resubmit_in_date_range(self, send_idv_update_mock): call_command('retry_failed_photo_verifications', status="submitted", diff --git a/lms/djangoapps/verify_student/management/commands/tests/test_trigger_softwaresecurephotoverifications_post_save_signal.py b/lms/djangoapps/verify_student/management/commands/tests/test_trigger_softwaresecurephotoverifications_post_save_signal.py index 99fd4ecd3a5f..487aaa936391 100644 --- a/lms/djangoapps/verify_student/management/commands/tests/test_trigger_softwaresecurephotoverifications_post_save_signal.py +++ b/lms/djangoapps/verify_student/management/commands/tests/test_trigger_softwaresecurephotoverifications_post_save_signal.py @@ -38,7 +38,7 @@ def _create_attempts(self, num_attempts): for _ in range(num_attempts): self.create_and_submit_attempt_for_user() - @patch('lms.djangoapps.verify_student.signals.idv_update_signal.send') + @patch('lms.djangoapps.verify_student.handlers.idv_update_signal.send') def test_command(self, send_idv_update_mock): call_command('trigger_softwaresecurephotoverifications_post_save_signal', start_date_time='2021-10-31 06:00:00') From a776150f7710cdcefc9188cb86cfbb53b573d486 Mon Sep 17 00:00:00 2001 From: ilee2u Date: Thu, 12 Sep 2024 15:02:39 -0400 Subject: [PATCH 05/13] fix: refactor signals and handlers pattern - following OEP-49 pattern for signals directory - user removed as param for update function - event now emitted after save --- lms/djangoapps/verify_student/api.py | 57 +++++++++---------- .../test_retry_failed_photo_verifications.py | 2 +- ...curephotoverifications_post_save_signal.py | 2 +- .../verify_student/{ => signals}/handlers.py | 8 +-- .../verify_student/{ => signals}/signals.py | 3 - .../verify_student/tests/test_api.py | 5 +- .../verify_student/tests/test_handlers.py | 7 ++- 7 files changed, 38 insertions(+), 46 deletions(-) rename lms/djangoapps/verify_student/{ => signals}/handlers.py (92%) rename lms/djangoapps/verify_student/{ => signals}/signals.py (92%) diff --git a/lms/djangoapps/verify_student/api.py b/lms/djangoapps/verify_student/api.py index 98f8432a375e..941dd60453d4 100644 --- a/lms/djangoapps/verify_student/api.py +++ b/lms/djangoapps/verify_student/api.py @@ -13,7 +13,7 @@ from lms.djangoapps.verify_student.emails import send_verification_approved_email from lms.djangoapps.verify_student.exceptions import VerificationAttemptInvalidStatus from lms.djangoapps.verify_student.models import VerificationAttempt -from lms.djangoapps.verify_student.signals import ( +from lms.djangoapps.verify_student.signals.signals import ( emit_idv_attempt_approved_event, emit_idv_attempt_created_event, emit_idv_attempt_denied_event, @@ -89,10 +89,9 @@ def create_verification_attempt(user: User, name: str, status: str, expiration_d def update_verification_attempt( attempt_id: int, - user: User, name: Optional[str] = None, status: Optional[str] = None, - expiration_datetime: Optional[datetime] = None + expiration_datetime: Optional[datetime] = None, ): """ Update a verification attempt. @@ -101,7 +100,6 @@ def update_verification_attempt( Arguments: * attempt_id (int): the verification attempt id of the attempt to update - * user (User): the user (usually a learner) performing the verification attempt * name (string, optional): the new name being ID verified * status (string, optional): the new status of the verification attempt * expiration_datetime (datetime, optional): The new expiration date and time @@ -136,33 +134,34 @@ def update_verification_attempt( ) raise VerificationAttemptInvalidStatus - if status == VerificationAttemptStatus.PENDING: - emit_idv_attempt_pending_event( - attempt_id=attempt_id, - user=user, - status=status, - name=name, - expiration_date=expiration_datetime, - ) - elif status == VerificationAttemptStatus.APPROVED: - emit_idv_attempt_approved_event( - attempt_id=attempt_id, - user=user, - status=status, - name=name, - expiration_date=expiration_datetime, - ) - elif status == VerificationAttemptStatus.DENIED: - emit_idv_attempt_denied_event( - attempt_id=attempt_id, - user=user, - status=status, - name=name, - expiration_date=expiration_datetime, - ) - # NOTE: Generally, we only set the expiration date from the time that an IDV attempt is marked approved, # so we allow expiration_datetime to = None for other status updates (e.g. pending). attempt.expiration_datetime = expiration_datetime attempt.save() + + user = attempt.user + if status == VerificationAttemptStatus.PENDING: + emit_idv_attempt_pending_event( + attempt_id=attempt_id, + user=user, + status=status, + name=name, + expiration_date=expiration_datetime, + ) + elif status == VerificationAttemptStatus.APPROVED: + emit_idv_attempt_approved_event( + attempt_id=attempt_id, + user=user, + status=status, + name=name, + expiration_date=expiration_datetime, + ) + elif status == VerificationAttemptStatus.DENIED: + emit_idv_attempt_denied_event( + attempt_id=attempt_id, + user=user, + status=status, + name=name, + expiration_date=expiration_datetime, + ) diff --git a/lms/djangoapps/verify_student/management/commands/tests/test_retry_failed_photo_verifications.py b/lms/djangoapps/verify_student/management/commands/tests/test_retry_failed_photo_verifications.py index cc1739f63fbe..8fa84efe3a85 100644 --- a/lms/djangoapps/verify_student/management/commands/tests/test_retry_failed_photo_verifications.py +++ b/lms/djangoapps/verify_student/management/commands/tests/test_retry_failed_photo_verifications.py @@ -121,7 +121,7 @@ def _create_attempts(self, num_attempts): for _ in range(num_attempts): self.create_upload_and_submit_attempt_for_user() - @patch('lms.djangoapps.verify_student.handlers.idv_update_signal.send') + @patch('lms.djangoapps.verify_student.signals.signals.idv_update_signal.send') def test_resubmit_in_date_range(self, send_idv_update_mock): call_command('retry_failed_photo_verifications', status="submitted", diff --git a/lms/djangoapps/verify_student/management/commands/tests/test_trigger_softwaresecurephotoverifications_post_save_signal.py b/lms/djangoapps/verify_student/management/commands/tests/test_trigger_softwaresecurephotoverifications_post_save_signal.py index 487aaa936391..c9e98a94dec0 100644 --- a/lms/djangoapps/verify_student/management/commands/tests/test_trigger_softwaresecurephotoverifications_post_save_signal.py +++ b/lms/djangoapps/verify_student/management/commands/tests/test_trigger_softwaresecurephotoverifications_post_save_signal.py @@ -38,7 +38,7 @@ def _create_attempts(self, num_attempts): for _ in range(num_attempts): self.create_and_submit_attempt_for_user() - @patch('lms.djangoapps.verify_student.handlers.idv_update_signal.send') + @patch('lms.djangoapps.verify_student.signals.signals.idv_update_signal.send') def test_command(self, send_idv_update_mock): call_command('trigger_softwaresecurephotoverifications_post_save_signal', start_date_time='2021-10-31 06:00:00') diff --git a/lms/djangoapps/verify_student/handlers.py b/lms/djangoapps/verify_student/signals/handlers.py similarity index 92% rename from lms/djangoapps/verify_student/handlers.py rename to lms/djangoapps/verify_student/signals/handlers.py index e08f107c44db..6ae57f715fab 100644 --- a/lms/djangoapps/verify_student/handlers.py +++ b/lms/djangoapps/verify_student/signals/handlers.py @@ -5,21 +5,19 @@ from django.core.exceptions import ObjectDoesNotExist from django.db.models.signals import post_save -from django.dispatch import Signal from django.dispatch.dispatcher import receiver from xmodule.modulestore.django import SignalHandler, modulestore from common.djangoapps.student.models_api import get_name, get_pending_name_change +from lms.djangoapps.verify_student.signals.signals import idv_update_signal +from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC +from lms.djangoapps.verify_student.apps import VerifyStudentConfig from .models import SoftwareSecurePhotoVerification, VerificationDeadline, VerificationAttempt log = logging.getLogger(__name__) -# Signal for emitting IDV submission and review updates -# providing_args = ["attempt_id", "user_id", "status", "full_name", "profile_name"] -idv_update_signal = Signal() - @receiver(SignalHandler.course_published) def _listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable=unused-argument diff --git a/lms/djangoapps/verify_student/signals.py b/lms/djangoapps/verify_student/signals/signals.py similarity index 92% rename from lms/djangoapps/verify_student/signals.py rename to lms/djangoapps/verify_student/signals/signals.py index a81b26b323ad..c03d5f263191 100644 --- a/lms/djangoapps/verify_student/signals.py +++ b/lms/djangoapps/verify_student/signals/signals.py @@ -22,9 +22,6 @@ def _create_user_data(user): Helper function to create a UserData object. """ user_data = UserData( - # NOTE to self: the id field was previously = lms_user_id in edx-exams, which I copied this from - # Do we want that specific ID, or is this fine? - # (There was also a "full_name" field, but I don't think we need that) id=user.id, is_active=user.is_active, pii=UserPersonalData( diff --git a/lms/djangoapps/verify_student/tests/test_api.py b/lms/djangoapps/verify_student/tests/test_api.py index 05ba5493a511..4497c877bbc4 100644 --- a/lms/djangoapps/verify_student/tests/test_api.py +++ b/lms/djangoapps/verify_student/tests/test_api.py @@ -15,6 +15,7 @@ send_approval_email, update_verification_attempt, ) +from lms.djangoapps.verify_student.signals.signals import idv_update_signal from lms.djangoapps.verify_student.exceptions import VerificationAttemptInvalidStatus from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationAttempt from lms.djangoapps.verify_student.statuses import VerificationAttemptStatus @@ -133,7 +134,6 @@ def test_update_verification_attempt(self, name, status, expiration_datetime): update_verification_attempt( attempt_id=self.attempt.id, name=name, - user=self.user, status=status, expiration_datetime=expiration_datetime, ) @@ -150,7 +150,6 @@ def test_update_verification_attempt_none_values(self): update_verification_attempt( attempt_id=self.attempt.id, name=None, - user=self.user, status=None, expiration_datetime=None, ) @@ -169,7 +168,6 @@ def test_update_verification_attempt_not_found(self): update_verification_attempt, attempt_id=999999, name=None, - user=self.user, status=VerificationAttemptStatus.APPROVED, ) @@ -185,7 +183,6 @@ def test_update_verification_attempt_invalid(self, status): update_verification_attempt, attempt_id=self.attempt.id, name=None, - user=self.user, status=status, expiration_datetime=None, ) diff --git a/lms/djangoapps/verify_student/tests/test_handlers.py b/lms/djangoapps/verify_student/tests/test_handlers.py index 34d0a6ccfc21..96bab7e1b27c 100644 --- a/lms/djangoapps/verify_student/tests/test_handlers.py +++ b/lms/djangoapps/verify_student/tests/test_handlers.py @@ -15,11 +15,12 @@ VerificationDeadline, VerificationAttempt ) -from lms.djangoapps.verify_student.handlers import ( +from lms.djangoapps.verify_student.signals.handlers import ( _listen_for_course_publish, _listen_for_lms_retire, _listen_for_lms_retire_verification_attempts ) +from lms.djangoapps.verify_student.signals.signals import idv_update_signal from lms.djangoapps.verify_student.tests.factories import ( SoftwareSecurePhotoVerificationFactory, VerificationAttemptFactory @@ -132,7 +133,7 @@ def setUp(self): self.photo_id_image_url = 'https://test.photo' self.photo_id_key = 'test+key' - @patch('lms.djangoapps.verify_student.handlers.idv_update_signal.send') + @patch('lms.djangoapps.verify_student.signals.signals.idv_update_signal.send') def test_post_save_signal(self, mock_signal): # create new softwaresecureverification attempt = SoftwareSecurePhotoVerification.objects.create( @@ -165,7 +166,7 @@ def test_post_save_signal(self, mock_signal): full_name=attempt.user.profile.name ) - @patch('lms.djangoapps.verify_student.handlers.idv_update_signal.send') + @patch('lms.djangoapps.verify_student.signals.signals.idv_update_signal.send') def test_post_save_signal_pending_name(self, mock_signal): pending_name_change = do_name_change_request(self.user, 'Pending Name', 'test')[0] From c430b9349ba6d67df979ecd1c26545fd22c6a0f6 Mon Sep 17 00:00:00 2001 From: ilee2u Date: Fri, 13 Sep 2024 15:24:28 -0400 Subject: [PATCH 06/13] fix: unpin edx-name-affirmation --- requirements/constraints.txt | 4 ---- requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/doc.txt | 2 +- requirements/edx/testing.txt | 2 +- 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index a87d41292189..c67cd7295323 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -142,7 +142,3 @@ django-storages<1.14.4 # We are pinning this until after all the smaller migrations get handled and then we can migrate this all at once. # Ticket to unpin: https://github.com/edx/edx-arch-experiments/issues/760 social-auth-app-django<=5.4.1 - -# Temporary pin as to prevent a new version of edx-name-affirmation from being merged before we modify it to work -# properly along with work in this PR: https://github.com/openedx/edx-platform/pull/35468 -edx-name-affirmation==2.4.0 diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 7f9f822de94d..9c6c6076ef42 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -482,7 +482,7 @@ edx-i18n-tools==1.5.0 # ora2 edx-milestones==0.6.0 # via -r requirements/edx/kernel.in -edx-name-affirmation==2.4.0 +edx-name-affirmation==2.4.1 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/kernel.in diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 0979f70d509c..b4219e7a9e88 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -766,7 +766,7 @@ edx-milestones==0.6.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -edx-name-affirmation==2.4.0 +edx-name-affirmation==2.4.1 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/doc.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 8b2302ebe319..02f9eb2393f4 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -562,7 +562,7 @@ edx-i18n-tools==1.5.0 # ora2 edx-milestones==0.6.0 # via -r requirements/edx/base.txt -edx-name-affirmation==2.4.0 +edx-name-affirmation==2.4.1 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 231fe7618867..35df4eb826cd 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -588,7 +588,7 @@ edx-lint==5.3.7 # via -r requirements/edx/testing.in edx-milestones==0.6.0 # via -r requirements/edx/base.txt -edx-name-affirmation==2.4.0 +edx-name-affirmation==2.4.1 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt From 2b8a31edc2fa1aafd8b3b293fa24b3fb5edec3ab Mon Sep 17 00:00:00 2001 From: ilee2u Date: Fri, 13 Sep 2024 15:28:09 -0400 Subject: [PATCH 07/13] chore: add init to signals dir --- lms/djangoapps/verify_student/signals/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 lms/djangoapps/verify_student/signals/__init__.py diff --git a/lms/djangoapps/verify_student/signals/__init__.py b/lms/djangoapps/verify_student/signals/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 From 0c2e6422e26ca03dfa2e7a75255136b922d869d2 Mon Sep 17 00:00:00 2001 From: ilee2u Date: Fri, 13 Sep 2024 16:39:21 -0400 Subject: [PATCH 08/13] fix: compile requirements --- requirements/edx/base.txt | 4 +--- requirements/edx/development.txt | 1 - requirements/edx/doc.txt | 4 +--- requirements/edx/testing.txt | 4 +--- 4 files changed, 3 insertions(+), 10 deletions(-) diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 9c6c6076ef42..3b22d175b4d9 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -483,9 +483,7 @@ edx-i18n-tools==1.5.0 edx-milestones==0.6.0 # via -r requirements/edx/kernel.in edx-name-affirmation==2.4.1 - # via - # -c requirements/edx/../constraints.txt - # -r requirements/edx/kernel.in + # via -r requirements/edx/kernel.in edx-opaque-keys[django]==2.11.0 # via # -r requirements/edx/kernel.in diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index b4219e7a9e88..8f894f916acf 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -768,7 +768,6 @@ edx-milestones==0.6.0 # -r requirements/edx/testing.txt edx-name-affirmation==2.4.1 # via - # -c requirements/edx/../constraints.txt # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt edx-opaque-keys[django]==2.11.0 diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 02f9eb2393f4..287873d5ea3c 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -563,9 +563,7 @@ edx-i18n-tools==1.5.0 edx-milestones==0.6.0 # via -r requirements/edx/base.txt edx-name-affirmation==2.4.1 - # via - # -c requirements/edx/../constraints.txt - # -r requirements/edx/base.txt + # via -r requirements/edx/base.txt edx-opaque-keys[django]==2.11.0 # via # -r requirements/edx/base.txt diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 35df4eb826cd..1a7773a61d1e 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -589,9 +589,7 @@ edx-lint==5.3.7 edx-milestones==0.6.0 # via -r requirements/edx/base.txt edx-name-affirmation==2.4.1 - # via - # -c requirements/edx/../constraints.txt - # -r requirements/edx/base.txt + # via -r requirements/edx/base.txt edx-opaque-keys[django]==2.11.0 # via # -r requirements/edx/base.txt From ba562027a22c5a83a1111e47fdc1830d98788e5a Mon Sep 17 00:00:00 2001 From: ilee2u Date: Mon, 16 Sep 2024 10:14:08 -0400 Subject: [PATCH 09/13] chore: quality --- lms/djangoapps/verify_student/signals/handlers.py | 2 -- lms/djangoapps/verify_student/tests/test_api.py | 1 - lms/djangoapps/verify_student/tests/test_handlers.py | 1 - 3 files changed, 4 deletions(-) diff --git a/lms/djangoapps/verify_student/signals/handlers.py b/lms/djangoapps/verify_student/signals/handlers.py index 6ae57f715fab..9ed2ef6695b1 100644 --- a/lms/djangoapps/verify_student/signals/handlers.py +++ b/lms/djangoapps/verify_student/signals/handlers.py @@ -11,9 +11,7 @@ from common.djangoapps.student.models_api import get_name, get_pending_name_change from lms.djangoapps.verify_student.signals.signals import idv_update_signal from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC -from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC -from lms.djangoapps.verify_student.apps import VerifyStudentConfig from .models import SoftwareSecurePhotoVerification, VerificationDeadline, VerificationAttempt log = logging.getLogger(__name__) diff --git a/lms/djangoapps/verify_student/tests/test_api.py b/lms/djangoapps/verify_student/tests/test_api.py index 4497c877bbc4..860978f0560e 100644 --- a/lms/djangoapps/verify_student/tests/test_api.py +++ b/lms/djangoapps/verify_student/tests/test_api.py @@ -15,7 +15,6 @@ send_approval_email, update_verification_attempt, ) -from lms.djangoapps.verify_student.signals.signals import idv_update_signal from lms.djangoapps.verify_student.exceptions import VerificationAttemptInvalidStatus from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationAttempt from lms.djangoapps.verify_student.statuses import VerificationAttemptStatus diff --git a/lms/djangoapps/verify_student/tests/test_handlers.py b/lms/djangoapps/verify_student/tests/test_handlers.py index 96bab7e1b27c..40d80712f19d 100644 --- a/lms/djangoapps/verify_student/tests/test_handlers.py +++ b/lms/djangoapps/verify_student/tests/test_handlers.py @@ -20,7 +20,6 @@ _listen_for_lms_retire, _listen_for_lms_retire_verification_attempts ) -from lms.djangoapps.verify_student.signals.signals import idv_update_signal from lms.djangoapps.verify_student.tests.factories import ( SoftwareSecurePhotoVerificationFactory, VerificationAttemptFactory From 71bd35803252255d2ebe44e091c33d09c7c14b52 Mon Sep 17 00:00:00 2001 From: ilee2u Date: Mon, 16 Sep 2024 10:42:05 -0400 Subject: [PATCH 10/13] chore: fix some imports --- lms/djangoapps/verify_student/apps.py | 2 +- lms/djangoapps/verify_student/signals/handlers.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/verify_student/apps.py b/lms/djangoapps/verify_student/apps.py index f01bdef7e908..d553b9e0cf9a 100644 --- a/lms/djangoapps/verify_student/apps.py +++ b/lms/djangoapps/verify_student/apps.py @@ -17,5 +17,5 @@ def ready(self): """ Connect signal handlers. """ - from lms.djangoapps.verify_student import signals # pylint: disable=unused-import + from lms.djangoapps.verify_student.signals import signals # pylint: disable=unused-import from lms.djangoapps.verify_student import tasks # pylint: disable=unused-import diff --git a/lms/djangoapps/verify_student/signals/handlers.py b/lms/djangoapps/verify_student/signals/handlers.py index 9ed2ef6695b1..4adb6f71d46c 100644 --- a/lms/djangoapps/verify_student/signals/handlers.py +++ b/lms/djangoapps/verify_student/signals/handlers.py @@ -9,10 +9,11 @@ from xmodule.modulestore.django import SignalHandler, modulestore from common.djangoapps.student.models_api import get_name, get_pending_name_change +from lms.djangoapps.verify_student.apps import VerifyStudentConfig # pylint: disable=unused-import from lms.djangoapps.verify_student.signals.signals import idv_update_signal from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC -from .models import SoftwareSecurePhotoVerification, VerificationDeadline, VerificationAttempt +from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline, VerificationAttempt log = logging.getLogger(__name__) From 05022b7f251ed084a3d0edc5f29df26458a775b1 Mon Sep 17 00:00:00 2001 From: ilee2u Date: Mon, 16 Sep 2024 11:04:12 -0400 Subject: [PATCH 11/13] chore: quality --- lms/djangoapps/verify_student/signals/handlers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/verify_student/signals/handlers.py b/lms/djangoapps/verify_student/signals/handlers.py index 4adb6f71d46c..8a1d7b542b00 100644 --- a/lms/djangoapps/verify_student/signals/handlers.py +++ b/lms/djangoapps/verify_student/signals/handlers.py @@ -13,7 +13,11 @@ from lms.djangoapps.verify_student.signals.signals import idv_update_signal from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC -from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline, VerificationAttempt +from lms.djangoapps.verify_student.models import ( + SoftwareSecurePhotoVerification, + VerificationDeadline, + VerificationAttempt +) log = logging.getLogger(__name__) From fc828daa44473fbc66348b5ecf7933ad528746eb Mon Sep 17 00:00:00 2001 From: ilee2u Date: Mon, 16 Sep 2024 13:24:59 -0400 Subject: [PATCH 12/13] test: added signal emissions to test_api --- .../verify_student/tests/test_api.py | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/verify_student/tests/test_api.py b/lms/djangoapps/verify_student/tests/test_api.py index 860978f0560e..ba4c107c0d91 100644 --- a/lms/djangoapps/verify_student/tests/test_api.py +++ b/lms/djangoapps/verify_student/tests/test_api.py @@ -69,7 +69,8 @@ def setUp(self): ) self.attempt.save() - def test_create_verification_attempt(self): + @patch('lms.djangoapps.verify_student.api.emit_idv_attempt_created_event') + def test_create_verification_attempt(self, mock_created_event): expected_id = 2 self.assertEqual( create_verification_attempt( @@ -86,6 +87,13 @@ def test_create_verification_attempt(self): self.assertEqual(verification_attempt.name, 'Tester McTest') self.assertEqual(verification_attempt.status, VerificationAttemptStatus.CREATED) self.assertEqual(verification_attempt.expiration_datetime, datetime(2024, 12, 31, tzinfo=timezone.utc)) + mock_created_event.assert_called_with( + attempt_id=verification_attempt.id, + user=self.user, + status=VerificationAttemptStatus.CREATED, + name='Tester McTest', + expiration_date=datetime(2024, 12, 31, tzinfo=timezone.utc), + ) def test_create_verification_attempt_no_expiration_datetime(self): expected_id = 2 @@ -129,7 +137,17 @@ def setUp(self): ('Tester McTest3', VerificationAttemptStatus.DENIED, datetime(2026, 12, 31, tzinfo=timezone.utc)), ) @ddt.unpack - def test_update_verification_attempt(self, name, status, expiration_datetime): + @patch('lms.djangoapps.verify_student.api.emit_idv_attempt_pending_event') + @patch('lms.djangoapps.verify_student.api.emit_idv_attempt_approved_event') + @patch('lms.djangoapps.verify_student.api.emit_idv_attempt_denied_event') + def test_update_verification_attempt(self, + name, + status, + expiration_datetime, + mock_denied_event, + mock_approved_event, + mock_pending_event, + ): update_verification_attempt( attempt_id=self.attempt.id, name=name, @@ -145,6 +163,31 @@ def test_update_verification_attempt(self, name, status, expiration_datetime): self.assertEqual(verification_attempt.status, status) self.assertEqual(verification_attempt.expiration_datetime, expiration_datetime) + if status == VerificationAttemptStatus.PENDING: + mock_pending_event.assert_called_with( + attempt_id=verification_attempt.id, + user=self.user, + status=status, + name=name, + expiration_date=expiration_datetime, + ) + elif status == VerificationAttemptStatus.APPROVED: + mock_approved_event.assert_called_with( + attempt_id=verification_attempt.id, + user=self.user, + status=status, + name=name, + expiration_date=expiration_datetime, + ) + elif status == VerificationAttemptStatus.DENIED: + mock_denied_event.assert_called_with( + attempt_id=verification_attempt.id, + user=self.user, + status=status, + name=name, + expiration_date=expiration_datetime, + ) + def test_update_verification_attempt_none_values(self): update_verification_attempt( attempt_id=self.attempt.id, From 8f3902fdf8903fa212674cf22be55000e24ac1e0 Mon Sep 17 00:00:00 2001 From: ilee2u Date: Mon, 16 Sep 2024 14:32:24 -0400 Subject: [PATCH 13/13] chore: lint --- .../verify_student/tests/test_api.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lms/djangoapps/verify_student/tests/test_api.py b/lms/djangoapps/verify_student/tests/test_api.py index ba4c107c0d91..2be7b6580905 100644 --- a/lms/djangoapps/verify_student/tests/test_api.py +++ b/lms/djangoapps/verify_student/tests/test_api.py @@ -88,11 +88,11 @@ def test_create_verification_attempt(self, mock_created_event): self.assertEqual(verification_attempt.status, VerificationAttemptStatus.CREATED) self.assertEqual(verification_attempt.expiration_datetime, datetime(2024, 12, 31, tzinfo=timezone.utc)) mock_created_event.assert_called_with( - attempt_id=verification_attempt.id, - user=self.user, - status=VerificationAttemptStatus.CREATED, - name='Tester McTest', - expiration_date=datetime(2024, 12, 31, tzinfo=timezone.utc), + attempt_id=verification_attempt.id, + user=self.user, + status=VerificationAttemptStatus.CREATED, + name='Tester McTest', + expiration_date=datetime(2024, 12, 31, tzinfo=timezone.utc), ) def test_create_verification_attempt_no_expiration_datetime(self): @@ -140,7 +140,8 @@ def setUp(self): @patch('lms.djangoapps.verify_student.api.emit_idv_attempt_pending_event') @patch('lms.djangoapps.verify_student.api.emit_idv_attempt_approved_event') @patch('lms.djangoapps.verify_student.api.emit_idv_attempt_denied_event') - def test_update_verification_attempt(self, + def test_update_verification_attempt( + self, name, status, expiration_datetime, @@ -170,7 +171,7 @@ def test_update_verification_attempt(self, status=status, name=name, expiration_date=expiration_datetime, - ) + ) elif status == VerificationAttemptStatus.APPROVED: mock_approved_event.assert_called_with( attempt_id=verification_attempt.id, @@ -178,7 +179,7 @@ def test_update_verification_attempt(self, status=status, name=name, expiration_date=expiration_datetime, - ) + ) elif status == VerificationAttemptStatus.DENIED: mock_denied_event.assert_called_with( attempt_id=verification_attempt.id, @@ -186,7 +187,7 @@ def test_update_verification_attempt(self, status=status, name=name, expiration_date=expiration_datetime, - ) + ) def test_update_verification_attempt_none_values(self): update_verification_attempt(