Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use idv approved event #35470

Merged
merged 3 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ def test_verification_signal(self):
"""
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()"
Expand Down
10 changes: 6 additions & 4 deletions lms/djangoapps/certificates/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@
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()

Expand Down Expand Up @@ -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']
Expand Down
19 changes: 12 additions & 7 deletions lms/djangoapps/certificates/tests/test_signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,20 @@
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
Copy link
Contributor Author

@Zacharis278 Zacharis278 Sep 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some imports moved due to running isort on this file

from xmodule.modulestore.tests.factories import CourseFactory


class SelfGeneratedCertsSignalTest(ModuleStoreTestCase):
Expand Down Expand Up @@ -302,10 +300,17 @@ def test_failing_grade_allowlist(self):
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def test_performance(self):
#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

Expand Down
44 changes: 33 additions & 11 deletions lms/djangoapps/verify_student/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@
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

Expand Down Expand Up @@ -248,13 +249,23 @@ def send_approval_signal(self, approved_by='None'):
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))


Expand Down Expand Up @@ -451,13 +462,24 @@ def approve(self, user_id=None, service=""):
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")
Expand Down
Loading