feat: use idv approved event (#35470)
* feat: replace LEARNER_NOW_VERIFIED signal with new openedx-event
This commit is contained in:
@@ -583,7 +583,7 @@ class SetIDVerificationStatusTestCase(TestCase):
|
||||
"""
|
||||
Verification signal is sent upon approval.
|
||||
"""
|
||||
with mock.patch('openedx.core.djangoapps.signals.signals.LEARNER_NOW_VERIFIED.send_robust') as mock_signal:
|
||||
with mock.patch('openedx_events.learning.signals.IDV_ATTEMPT_APPROVED.send_event') as mock_signal:
|
||||
# Begin the pipeline.
|
||||
pipeline.set_id_verification_status(
|
||||
auth_entry=pipeline.AUTH_ENTRY_LOGIN,
|
||||
|
||||
@@ -31,7 +31,7 @@ workspace {
|
||||
}
|
||||
|
||||
grades_app -> signal_handlers "Emits COURSE_GRADE_NOW_PASSED signal"
|
||||
verify_student_app -> signal_handlers "Emits LEARNER_NOW_VERIFIED signal"
|
||||
verify_student_app -> signal_handlers "Emits IDV_ATTEMPT_APPROVED signal"
|
||||
student_app -> signal_handlers "Emits ENROLLMENT_TRACK_UPDATED signal"
|
||||
allowlist -> signal_handlers "Emits APPEND_CERTIFICATE_ALLOWLIST signal"
|
||||
signal_handlers -> generation_handler "Invokes generate_allowlist_certificate()"
|
||||
|
||||
@@ -32,9 +32,8 @@ from openedx.core.djangoapps.content.course_overviews.signals import COURSE_PACI
|
||||
from openedx.core.djangoapps.signals.signals import (
|
||||
COURSE_GRADE_NOW_FAILED,
|
||||
COURSE_GRADE_NOW_PASSED,
|
||||
LEARNER_NOW_VERIFIED
|
||||
)
|
||||
from openedx_events.learning.signals import EXAM_ATTEMPT_REJECTED
|
||||
from openedx_events.learning.signals import EXAM_ATTEMPT_REJECTED, IDV_ATTEMPT_APPROVED
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -118,14 +117,17 @@ def _listen_for_failing_grade(sender, user, course_id, grade, **kwargs): # pyli
|
||||
log.info(f'Certificate marked not passing for {user.id} : {course_id} via failing grade')
|
||||
|
||||
|
||||
@receiver(LEARNER_NOW_VERIFIED, dispatch_uid="learner_track_changed")
|
||||
def _listen_for_id_verification_status_changed(sender, user, **kwargs): # pylint: disable=unused-argument
|
||||
@receiver(IDV_ATTEMPT_APPROVED, dispatch_uid="learner_track_changed")
|
||||
def _listen_for_id_verification_status_changed(sender, signal, **kwargs): # pylint: disable=unused-argument
|
||||
"""
|
||||
Listen for a signal indicating that the user's id verification status has changed.
|
||||
"""
|
||||
if not auto_certificate_generation_enabled():
|
||||
return
|
||||
|
||||
event_data = kwargs.get('idv_attempt')
|
||||
user = User.objects.get(id=event_data.user.id)
|
||||
|
||||
user_enrollments = CourseEnrollment.enrollments_for_user(user=user)
|
||||
expected_verification_status = IDVerificationService.user_status(user)
|
||||
expected_verification_status = expected_verification_status['status']
|
||||
|
||||
@@ -13,22 +13,20 @@ from opaque_keys.edx.keys import CourseKey, UsageKey
|
||||
from openedx_events.data import EventsMetadata
|
||||
from openedx_events.learning.data import ExamAttemptData, UserData, UserPersonalData
|
||||
from openedx_events.learning.signals import EXAM_ATTEMPT_REJECTED
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from openedx_events.tests.utils import OpenEdxEventsTestMixin
|
||||
|
||||
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from lms.djangoapps.certificates.api import has_self_generated_certificates_enabled
|
||||
from lms.djangoapps.certificates.config import AUTO_CERTIFICATE_GENERATION
|
||||
from lms.djangoapps.certificates.data import CertificateStatuses
|
||||
from lms.djangoapps.certificates.models import (
|
||||
CertificateGenerationConfiguration,
|
||||
GeneratedCertificate
|
||||
)
|
||||
from lms.djangoapps.certificates.models import CertificateGenerationConfiguration, GeneratedCertificate
|
||||
from lms.djangoapps.certificates.signals import handle_exam_attempt_rejected_event
|
||||
from lms.djangoapps.certificates.tests.factories import CertificateAllowlistFactory, GeneratedCertificateFactory
|
||||
from lms.djangoapps.grades.course_grade_factory import CourseGradeFactory
|
||||
from lms.djangoapps.grades.tests.utils import mock_passing_grade
|
||||
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
class SelfGeneratedCertsSignalTest(ModuleStoreTestCase):
|
||||
@@ -302,10 +300,17 @@ class FailingGradeCertsTest(ModuleStoreTestCase):
|
||||
assert cert.status == CertificateStatuses.downloadable
|
||||
|
||||
|
||||
class LearnerIdVerificationTest(ModuleStoreTestCase):
|
||||
class LearnerIdVerificationTest(ModuleStoreTestCase, OpenEdxEventsTestMixin):
|
||||
"""
|
||||
Tests for certificate generation task firing on learner id verification
|
||||
"""
|
||||
ENABLED_OPENEDX_EVENTS = ['org.openedx.learning.idv_attempt.approved.v1']
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.start_events_isolation()
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.course_one = CourseFactory.create(self_paced=True)
|
||||
|
||||
@@ -54,7 +54,7 @@ class TestBackfillSSOVerificationsCommand(TestCase):
|
||||
#self.assertNumQueries(100)
|
||||
|
||||
def test_signal_called(self):
|
||||
with patch('openedx.core.djangoapps.signals.signals.LEARNER_NOW_VERIFIED.send_robust') as mock_signal:
|
||||
with patch('openedx_events.learning.signals.IDV_ATTEMPT_APPROVED.send_event') as mock_signal:
|
||||
call_command('backfill_sso_verifications_for_old_account_links', '--provider-slug', self.provider.provider_id) # lint-amnesty, pylint: disable=line-too-long
|
||||
assert mock_signal.call_count == 1
|
||||
|
||||
|
||||
@@ -42,8 +42,9 @@ from lms.djangoapps.verify_student.ssencrypt import (
|
||||
rsa_decrypt,
|
||||
rsa_encrypt
|
||||
)
|
||||
from openedx.core.djangoapps.signals.signals import LEARNER_NOW_VERIFIED
|
||||
from openedx.core.storage import get_storage
|
||||
from openedx_events.learning.signals import IDV_ATTEMPT_APPROVED
|
||||
from openedx_events.learning.data import UserData, VerificationAttemptData
|
||||
|
||||
from .utils import auto_verify_for_testing_enabled, earliest_allowed_verification_date, submit_request_to_ss
|
||||
|
||||
@@ -248,13 +249,23 @@ class SSOVerification(IDVerificationAttempt):
|
||||
user_id=self.user, reviewer=approved_by
|
||||
))
|
||||
|
||||
# Emit signal to find and generate eligible certificates
|
||||
LEARNER_NOW_VERIFIED.send_robust(
|
||||
sender=SSOVerification,
|
||||
user=self.user
|
||||
# Emit event to find and generate eligible certificates
|
||||
verification_data = VerificationAttemptData(
|
||||
attempt_id=self.id,
|
||||
user=UserData(
|
||||
pii=None,
|
||||
id=self.user.id,
|
||||
is_active=self.user.is_active,
|
||||
),
|
||||
status=self.status,
|
||||
name=self.name,
|
||||
expiration_date=self.expiration_datetime,
|
||||
)
|
||||
IDV_ATTEMPT_APPROVED.send_event(
|
||||
idv_attempt=verification_data,
|
||||
)
|
||||
|
||||
message = 'LEARNER_NOW_VERIFIED signal fired for {user} from SSOVerification'
|
||||
message = 'IDV_ATTEMPT_APPROVED signal fired for {user} from SSOVerification'
|
||||
log.info(message.format(user=self.user.username))
|
||||
|
||||
|
||||
@@ -451,13 +462,24 @@ class PhotoVerification(IDVerificationAttempt):
|
||||
days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"]
|
||||
)
|
||||
self.save()
|
||||
# Emit signal to find and generate eligible certificates
|
||||
LEARNER_NOW_VERIFIED.send_robust(
|
||||
sender=PhotoVerification,
|
||||
user=self.user
|
||||
|
||||
# Emit event to find and generate eligible certificates
|
||||
verification_data = VerificationAttemptData(
|
||||
attempt_id=self.id,
|
||||
user=UserData(
|
||||
pii=None,
|
||||
id=self.user.id,
|
||||
is_active=self.user.is_active,
|
||||
),
|
||||
status=self.status,
|
||||
name=self.name,
|
||||
expiration_date=self.expiration_datetime,
|
||||
)
|
||||
IDV_ATTEMPT_APPROVED.send_event(
|
||||
idv_attempt=verification_data,
|
||||
)
|
||||
|
||||
message = 'LEARNER_NOW_VERIFIED signal fired for {user} from PhotoVerification'
|
||||
message = 'IDV_ATTEMPT_APPROVED signal fired for {user} from PhotoVerification'
|
||||
log.info(message.format(user=self.user.username))
|
||||
|
||||
@status_before_must_be("ready", "must_retry")
|
||||
|
||||
@@ -36,9 +36,5 @@ COURSE_GRADE_NOW_PASSED = Signal()
|
||||
# ]
|
||||
COURSE_GRADE_NOW_FAILED = Signal()
|
||||
|
||||
# Signal that indicates that a user has become verified for certificate purposes
|
||||
# providing_args=['user']
|
||||
LEARNER_NOW_VERIFIED = Signal()
|
||||
|
||||
# providing_args=['user']
|
||||
USER_ACCOUNT_ACTIVATED = Signal() # Signal indicating email verification
|
||||
|
||||
Reference in New Issue
Block a user