예제 #1
0
def _listen_for_id_verification_status_changed(sender, user, **kwargs):  # pylint: disable=unused-argument
    """
    Listen for a signal indicating that the user's id verification status has changed.

    If needed, generate a certificate task.
    """
    if not auto_certificate_generation_enabled():
        return

    user_enrollments = CourseEnrollment.enrollments_for_user(user=user)

    grade_factory = CourseGradeFactory()
    expected_verification_status = IDVerificationService.user_status(user)
    expected_verification_status = expected_verification_status['status']
    for enrollment in user_enrollments:
        if can_generate_certificate_task(user, enrollment.course_id):
            log.info(
                f'{enrollment.course_id} is using V2 certificates. Attempt will be made to generate a V2 '
                f'certificate for {user.id}. Id verification status is {expected_verification_status}'
            )
            generate_certificate_task(user, enrollment.course_id)
        elif grade_factory.read(user=user,
                                course=enrollment.course_overview).passed:
            if _fire_ungenerated_certificate_task(
                    user, enrollment.course_id, expected_verification_status):
                message = (
                    'Certificate generation task initiated for {user} : {course} via track change '
                    + 'with verification status of {status}')
                log.info(
                    message.format(user=user.id,
                                   course=enrollment.course_id,
                                   status=expected_verification_status))
예제 #2
0
    def handle(self, *args, **options):
        if not options.get('user'):
            raise CommandError('You must specify a list of users')

        course_key = options.get('course_key')
        if not course_key:
            raise CommandError('You must specify a course-key')

        # Parse the serialized course key into a CourseKey
        try:
            course_key = CourseKey.from_string(course_key)
        except InvalidKeyError as e:
            raise CommandError('You must specify a valid course-key') from e

        # Loop over each user, and ask that a cert be generated for them
        users_str = options['user']
        for user_id in users_str:
            user = None
            try:
                user = User.objects.get(id=user_id)
            except User.DoesNotExist:
                log.warning(
                    'User {user} could not be found'.format(user=user_id))
            if user is not None:
                log.info(
                    'Calling generate_certificate_task for {user} : {course}'.
                    format(user=user.id, course=course_key))
                generate_certificate_task(user, course_key)
예제 #3
0
def _listen_for_enrollment_mode_change(sender, user, course_key, mode, **kwargs):  # pylint: disable=unused-argument
    """
    Listen for the signal indicating that a user's enrollment mode has changed.

    If possible, grant the user a course certificate. Note that we intentionally do not revoke certificates here, even
    if the user has moved to the audit track.
    """
    if modes_api.is_eligible_for_certificate(mode):
        if can_generate_certificate_task(user, course_key):
            log.info(f'{course_key} is using V2 certificates. Attempt will be made to generate a V2 certificate for '
                     f'{user.id} since the enrollment mode is now {mode}.')
            generate_certificate_task(user, course_key)
def _listen_for_id_verification_status_changed(sender, user, **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

    user_enrollments = CourseEnrollment.enrollments_for_user(user=user)
    expected_verification_status = IDVerificationService.user_status(user)
    expected_verification_status = expected_verification_status['status']

    for enrollment in user_enrollments:
        log.info(
            f'Attempt will be made to generate a course certificate for {user.id} : {enrollment.course_id}. Id '
            f'verification status is {expected_verification_status}')
        generate_certificate_task(user, enrollment.course_id)
예제 #5
0
def listen_for_passing_grade(sender, user, course_id, **kwargs):  # pylint: disable=unused-argument
    """
    Listen for a signal indicating that the user has passed a course run.

    If needed, generate a certificate task.
    """
    if not auto_certificate_generation_enabled():
        return

    cert = GeneratedCertificate.certificate_for_student(user, course_id)
    if cert is not None and CertificateStatuses.is_passing_status(cert.status):
        log.info(
            f'The cert status is already passing for user {user.id} : {course_id}. Passing grade signal will be '
            f'ignored.')
        return
    log.info(
        f'Attempt will be made to generate a course certificate for {user.id} : {course_id} as a passing grade '
        f'was received.')
    try:
        return generate_certificate_task(user, course_id)
    except CertificateGenerationNotAllowed as e:
        log.exception(
            "Certificate generation not allowed for user %s in course %s",
            str(user),
            course_id,
        )
        return False
예제 #6
0
def listen_for_passing_grade(sender, user, course_id, **kwargs):  # pylint: disable=unused-argument
    """
    Listen for a signal indicating that the user has passed a course run.

    If needed, generate a certificate task.
    """
    if not auto_certificate_generation_enabled():
        return

    if can_generate_certificate_task(user, course_id):
        cert = GeneratedCertificate.certificate_for_student(user, course_id)
        if cert is not None and CertificateStatuses.is_passing_status(
                cert.status):
            log.info(
                f'{course_id} is using V2 certificates, and the cert status is already passing for user '
                f'{user.id}. Passing grade signal will be ignored.')
            return
        log.info(
            f'{course_id} is using V2 certificates. Attempt will be made to generate a V2 certificate for '
            f'{user.id} as a passing grade was received.')
        return generate_certificate_task(user, course_id)

    if _fire_ungenerated_certificate_task(user, course_id):
        log.info(
            'Certificate generation task initiated for {user} : {course} via passing grade'
            .format(user=user.id, course=course_id))
 def test_handle_valid(self):
     """
     Test handling of a valid user/course run combo.
     """
     assert _can_generate_v2_certificate(self.user, self.course_run_key)
     assert can_generate_certificate_task(self.user, self.course_run_key)
     assert generate_certificate_task(self.user, self.course_run_key)
예제 #8
0
def _regenerate_certs(certs, batch_size, sleep_seconds, count):
    """
    Triggers generate certificate task for a given set of certificates
    """
    for cert in certs:
        user = User.objects.get(id=cert.user_id)
        generate_certificate_task(user,
                                  cert.course_id,
                                  generation_mode='batch',
                                  delay_seconds=0)
        count += 1
        if count % batch_size == 0:
            log.info(
                f'Regenerated {count} unverified certificates. Sleeping for {sleep_seconds} seconds.'
            )
            time.sleep(sleep_seconds)
    return count
예제 #9
0
 def test_handle_invalid(self):
     """
     Test handling of an invalid user/course run combo
     """
     assert not _can_generate_v2_certificate(self.user, self.course_run_key)
     assert not can_generate_certificate_task(self.user, self.course_run_key)
     assert not generate_certificate_task(self.user, self.course_run_key)
     assert not generate_regular_certificate_task(self.user, self.course_run_key)
예제 #10
0
    def test_certificate_creation_filter_prevent_generation(self):
        """
        Test prevent the user's certificate generation through a pipeline step.

        Expected result:
            - CertificateCreationRequested is triggered and executes TestStopCertificateGenerationStep.
            - The certificate is not generated.
        """
        with self.assertRaises(CertificateGenerationNotAllowed):
            generate_certificate_task(
                self.user, self.course_run.id, generation_mode=CourseMode.HONOR,
            )

        self.assertFalse(
            GeneratedCertificate.objects.filter(
                user=self.user, course_id=self.course_run.id, mode=CourseMode.HONOR,
            )
        )
예제 #11
0
def _regenerate_certs(certs, batch_size, sleep_seconds, count, check_integrity_signature_flag):
    """
    Triggers generate certificate task for a given set of certificates
    """
    for cert in certs:
        # not ideal to replicate this check, but we have to in the case that we are regenerating certs for all courses.
        if check_integrity_signature_flag and not is_integrity_signature_enabled(cert.course_id):
            log.warning(
                f'Skipping regenerating cert_id={cert.id} because {cert.course_id} does not have honor code enabled'
            )
        else:
            user = User.objects.get(id=cert.user_id)
            generate_certificate_task(user, cert.course_id, generation_mode='batch', delay_seconds=0)
            count += 1
            if count % batch_size == 0:
                log.info(f'Regenerated {count} unverified certificates. Sleeping for {sleep_seconds} seconds.')
                time.sleep(sleep_seconds)
    return count
 def test_handle_valid(self):
     """
     Test handling of a valid user/course run combo.
     """
     assert _can_generate_regular_certificate(self.user,
                                              self.course_run_key,
                                              self.enrollment_mode,
                                              self.grade)
     assert generate_certificate_task(self.user, self.course_run_key)
 def test_handle_invalid(self):
     """
     Test handling of an invalid user/course run combo
     """
     other_user = UserFactory()
     assert not _can_generate_v2_certificate(other_user,
                                             self.course_run_key)
     assert not generate_certificate_task(other_user, self.course_run_key)
     assert not generate_regular_certificate_task(other_user,
                                                  self.course_run_key)
    def test_handle_invalid(self):
        """
        Test handling of an invalid user/course run combo
        """
        u = UserFactory()

        assert not _can_generate_allowlist_certificate(u, self.course_run_key)
        assert not generate_allowlist_certificate_task(u, self.course_run_key)
        assert not generate_certificate_task(u, self.course_run_key)
        assert _set_allowlist_cert_status(u, self.course_run_key) is None
예제 #15
0
    def test_handle_valid(self):
        """
        Test handling of a valid user/course run combo.

        Note: these assertions are placeholders for now. They will be updated as the implementation is added.
        """
        assert not _can_generate_v2_certificate(self.user, self.course_run_key)
        assert can_generate_certificate_task(self.user, self.course_run_key)
        assert not generate_certificate_task(self.user, self.course_run_key)
        assert not generate_regular_certificate_task(self.user, self.course_run_key)
예제 #16
0
    def handle(self, *args, **options):
        # database args will override cmd line args
        if options['args_from_database']:
            options = self.get_args_from_database()

        if not options.get('user'):
            raise CommandError('You must specify a list of users')

        course_key = options.get('course_key')
        if not course_key:
            raise CommandError('You must specify a course-key')

        # Parse the serialized course key into a CourseKey
        try:
            course_key = CourseKey.from_string(course_key)
        except InvalidKeyError as e:
            raise CommandError('You must specify a valid course-key') from e

        # Loop over each user, and ask that a cert be generated for them
        users_str = options['user']
        for user_id in users_str:
            user = None
            try:
                user = User.objects.get(id=user_id)
            except User.DoesNotExist:
                log.warning(f'User {user_id} could not be found')
            if user is not None:
                log.info(
                    'Calling generate_certificate_task for {user} : {course}'.
                    format(user=user.id, course=course_key))
                try:
                    generate_certificate_task(user, course_key)
                except CertificateGenerationNotAllowed as e:
                    log.exception(
                        "Certificate generation not allowed for user %s in course %s",
                        user.id,
                        course_key,
                    )
예제 #17
0
def listen_for_passing_grade(sender, user, course_id, **kwargs):  # pylint: disable=unused-argument
    """
    Listen for a learner passing a course, send cert generation task,
    downstream signal from COURSE_GRADE_CHANGED
    """
    if not auto_certificate_generation_enabled():
        return

    if can_generate_certificate_task(user, course_id):
        log.info(f'{course_id} is using V2 certificates. Attempt will be made to generate a V2 certificate for '
                 f'{user.id} as a passing grade was received.')
        return generate_certificate_task(user, course_id)

    if _fire_ungenerated_certificate_task(user, course_id):
        log.info('Certificate generation task initiated for {user} : {course} via passing grade'.format(
            user=user.id,
            course=course_id
        ))
예제 #18
0
    def test_certificate_generation_without_filter_configuration(self):
        """
        Test usual certificate process, without filter's intervention.

        Expected result:
            - CertificateCreationRequested does not have any effect on the certificate generation process.
            - The certificate generation process ends successfully.
        """
        cert_gen_task_created = generate_certificate_task(
            self.user, self.course_run.id, generation_mode=CourseMode.HONOR,
        )

        certificate = GeneratedCertificate.objects.get(
            user=self.user,
            course_id=self.course_run.id,
        )

        self.assertTrue(cert_gen_task_created)
        self.assertEqual(CourseMode.HONOR, certificate.mode)
예제 #19
0
    def test_certificate_creation_filter_executed(self):
        """
        Test whether the student certificate filter is triggered before the user's
        certificate creation process.

        Expected result:
            - CertificateCreationRequested is triggered and executes TestCertificatePipelineStep.
            - The certificate generates with no-id-professional mode instead of honor mode.
        """
        cert_gen_task_created = generate_certificate_task(
            self.user, self.course_run.id, generation_mode=CourseMode.HONOR,
        )

        certificate = GeneratedCertificate.objects.get(
            user=self.user,
            course_id=self.course_run.id,
        )

        self.assertTrue(cert_gen_task_created)
        self.assertEqual(CourseMode.NO_ID_PROFESSIONAL_MODE, certificate.mode)
예제 #20
0
def _listen_for_enrollment_mode_change(sender, user, course_key, mode,
                                       **kwargs):  # pylint: disable=unused-argument
    """
    Listen for the signal indicating that a user's enrollment mode has changed.

    If possible, grant the user a course certificate. Note that we intentionally do not revoke certificates here, even
    if the user has moved to the audit track.
    """
    if modes_api.is_eligible_for_certificate(mode):
        log.info(
            f'Attempt will be made to generate a course certificate for {user.id} : {course_key} since the '
            f'enrollment mode is now {mode}.')
        try:
            return generate_certificate_task(user, course_key)
        except CertificateGenerationNotAllowed as e:
            log.exception(
                "Certificate generation not allowed for user %s in course %s",
                str(user),
                course_key,
            )
            return False
예제 #21
0
 def test_handle_valid_general_methods(self):
     """
     Test handling of a valid user/course run combo for the general (non-allowlist) generation methods
     """
     assert can_generate_certificate_task(self.user, self.course_run_key)
     assert generate_certificate_task(self.user, self.course_run_key)
예제 #22
0
 def test_handle_no_grade(self):
     """
     Test handling when the grade is none
     """
     with mock.patch(GET_GRADE_METHOD, return_value=None):
         assert generate_certificate_task(self.user, self.course_run_key)