def test_user_status(self): # test for correct status when no error returned user = UserFactory.create() status = IDVerificationService.user_status(user) self.assertEquals(status, ('none', '')) # test for when one has been created attempt = SoftwareSecurePhotoVerification.objects.create( user=user, status='approved') status = IDVerificationService.user_status(user) self.assertEquals(status, ('approved', '')) # create another one for the same user, make sure the right one is # returned SoftwareSecurePhotoVerification.objects.create( user=user, status='denied', error_msg='[{"photoIdReasons": ["Not provided"]}]') status = IDVerificationService.user_status(user) self.assertEquals(status, ('approved', '')) # now delete the first one and verify that the denial is being handled # properly attempt.delete() status = IDVerificationService.user_status(user) self.assertEquals(status, ('must_reverify', ['id_image_missing']))
def test_user_status(self): # test for correct status when no error returned user = UserFactory.create() status = IDVerificationService.user_status(user) self.assertEquals(status, {'status': 'none', 'error': '', 'should_display': True}) # test for when photo verification has been created SoftwareSecurePhotoVerification.objects.create(user=user, status='approved') status = IDVerificationService.user_status(user) self.assertEquals(status, {'status': 'approved', 'error': '', 'should_display': True}) # create another photo verification for the same user, make sure the denial # is handled properly SoftwareSecurePhotoVerification.objects.create( user=user, status='denied', error_msg='[{"photoIdReasons": ["Not provided"]}]' ) status = IDVerificationService.user_status(user) self.assertEquals(status, {'status': 'must_reverify', 'error': ['id_image_missing'], 'should_display': True}) # test for when sso verification has been created SSOVerification.objects.create(user=user, status='approved') status = IDVerificationService.user_status(user) self.assertEquals(status, {'status': 'approved', 'error': '', 'should_display': False}) # create another sso verification for the same user, make sure the denial # is handled properly SSOVerification.objects.create(user=user, status='denied') status = IDVerificationService.user_status(user) self.assertEquals(status, {'status': 'must_reverify', 'error': '', 'should_display': False})
def _listen_for_id_verification_status_changed(sender, user, **kwargs): # pylint: disable=unused-argument """ Catches a track change signal, determines user status, calls fire_ungenerated_certificate_task for passing grades """ 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 grade_factory.read(user=user, course=enrollment.course_overview).passed: if fire_ungenerated_certificate_task(user, enrollment.course_id, expected_verification_status): message = ( u'Certificate generation task initiated for {user} : {course} via track change ' + u'with verification status of {status}' ) log.info(message.format( user=user.id, course=enrollment.course_id, status=expected_verification_status ))
def generate_certificate(self, **kwargs): """ Generates a certificate for a single user. kwargs: - student: The student for whom to generate a certificate. - course_key: The course key for the course that the student is receiving a certificate in. - expected_verification_status: The expected verification status for the user. When the status has changed, we double check that the actual verification status is as expected before generating a certificate, in the off chance that the database has not yet updated with the user's new verification status. """ original_kwargs = kwargs.copy() student = User.objects.get(id=kwargs.pop('student')) course_key = CourseKey.from_string(kwargs.pop('course_key')) expected_verification_status = kwargs.pop('expected_verification_status', None) if expected_verification_status: actual_verification_status = IDVerificationService.user_status(student) if expected_verification_status != actual_verification_status: raise self.retry(kwargs=original_kwargs) generate_user_certificates(student=student, course_key=course_key, **kwargs)
def test_expired_verification(self): with freeze_time('2015-07-11') as frozen_datetime: # create approved photo verification for the user SoftwareSecurePhotoVerification.objects.create( user=self.user, status='approved', expiration_date=now() + timedelta(days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"])) frozen_datetime.move_to('2016-07-11') expected_status = { 'status': 'expired', 'error': _("Your {platform_name} verification has expired.").format( platform_name=configuration_helpers.get_value( 'platform_name', settings.PLATFORM_NAME)), 'should_display': True, 'verification_expiry': '', 'status_date': '' } status = IDVerificationService.user_status(self.user) self.assertDictEqual(status, expected_status)
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))
def _get_external_user_info(self, external_user_key, org_key): """ Provided the external_user_key and org_key, return edx account info and program_enrollments_info if any. If we cannot identify the data, return empty object. """ found_user = None result = {} try: users_by_key = get_users_by_external_keys_and_org_key( [external_user_key], org_key) found_user = users_by_key.get(external_user_key) except (BadOrganizationShortNameException, ProviderConfigurationException, ProviderDoesNotExistException): # We cannot identify edX user from external_user_key and org_key pair pass if found_user: try: user_social_auth = UserSocialAuth.objects.get(user=found_user) except UserSocialAuth.DoesNotExist: user_social_auth = None user_info = serialize_user_info(found_user, user_social_auth) result['user'] = user_info result['id_verification'] = IDVerificationService.user_status( found_user) enrollments = self._get_enrollments( external_user_key=external_user_key) if enrollments: result['enrollments'] = enrollments return result
def generate_certificate(self, **kwargs): """ Generates a certificate for a single user. kwargs: - student: The student for whom to generate a certificate. - course_key: The course key for the course that the student is receiving a certificate in. - expected_verification_status: The expected verification status for the user. When the status has changed, we double check that the actual verification status is as expected before generating a certificate, in the off chance that the database has not yet updated with the user's new verification status. """ original_kwargs = kwargs.copy() student = User.objects.get(id=kwargs.pop('student')) course_key = CourseKey.from_string(kwargs.pop('course_key')) expected_verification_status = kwargs.pop('expected_verification_status', None) if expected_verification_status: actual_verification_status = IDVerificationService.user_status(student) actual_verification_status = actual_verification_status['status'] if expected_verification_status != actual_verification_status: logger.warn('Expected verification status {expected} ' 'differs from actual verification status {actual} ' 'for user {user} in course {course}'.format( expected=expected_verification_status, actual=actual_verification_status, user=student.id, course=course_key )) raise self.retry(kwargs=original_kwargs) generate_user_certificates(student=student, course_key=course_key, **kwargs)
def test_expired_verification(self, new_status): with freeze_time('2015-07-11') as frozen_datetime: # create approved photo verification for the user SoftwareSecurePhotoVerification.objects.create(user=self.user, status='approved') frozen_datetime.move_to('2015-07-22') # create another according to status passed in. SoftwareSecurePhotoVerification.objects.create(user=self.user, status=new_status) check_status = new_status status_date = '' if new_status in ('submitted', 'must_retry'): check_status = 'pending' elif new_status in ('created', 'ready'): check_status = 'none' elif new_status == 'denied': check_status = 'must_reverify' else: status_date = datetime.now(utc) expected_status = { 'status': check_status, 'error': '', 'should_display': True, 'verification_expiry': '', 'status_date': status_date } status = IDVerificationService.user_status(self.user) self.assertDictEqual(status, expected_status)
def _get_account_info(self, username_or_email, idp_provider=None): """ Provided the edx account username or email, and the SAML provider selected, return edx account info and program_enrollments_info. If we cannot identify the user, return empty object and error. """ try: user = User.objects.get( Q(username=username_or_email) | Q(email=username_or_email)) user_social_auths = None user_social_auths = UserSocialAuth.objects.filter(user=user) if idp_provider: user_social_auths = user_social_auths.filter( provider=idp_provider.backend_name) user_info = serialize_user_info(user, user_social_auths) enrollments = self._get_enrollments(user=user) result = {'user': user_info} if enrollments: result['enrollments'] = enrollments result['id_verification'] = IDVerificationService.user_status(user) return result, '' except User.DoesNotExist: return {}, 'Could not find edx account with {}'.format( username_or_email)
def _listen_for_id_verification_status_changed(sender, user, **kwargs): # pylint: disable=unused-argument """ Catches a track change signal, determines user status, calls _fire_ungenerated_certificate_task for passing grades """ 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 is_using_certificate_allowlist_and_is_on_allowlist( user, enrollment.course_id): log.info( f'{enrollment.course_id} is using allowlist certificates, and the user {user.id} is on its ' f'allowlist. Attempt will be made to generate an allowlist certificate. Id verification status ' f'is {expected_verification_status}') generate_allowlist_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 = ( u'Certificate generation task initiated for {user} : {course} via track change ' + u'with verification status of {status}') log.info( message.format(user=user.id, course=enrollment.course_id, status=expected_verification_status))
def test_manual_verification(self): with freeze_time('2015-05-02'): # test for when manual verification has been created ManualVerification.objects.create(user=self.user, status='approved') status = IDVerificationService.user_status(self.user) expected_status = {'status': 'approved', 'error': '', 'should_display': False, 'verification_expiry': '', 'status_date': datetime.now(utc)} self.assertDictEqual(status, expected_status)
def test_approved_software_secure_verification(self): with freeze_time('2015-01-02'): # test for when photo verification has been created SoftwareSecurePhotoVerification.objects.create(user=self.user, status='approved') status = IDVerificationService.user_status(self.user) expected_status = {'status': 'approved', 'error': '', 'should_display': True, 'verification_expiry': '', 'status_date': datetime.now(utc)} self.assertDictEqual(status, expected_status)
def verification_status(self): """ Returns a String of the verification status of learner. """ if self.enrollment_object and self.enrollment_object.mode in CourseMode.VERIFIED_MODES: return IDVerificationService.user_status(self.effective_user)['status'] # I know this looks weird (and is), but this is just so it is inline with what the # IDVerificationService.user_status method would return before a verification was created return 'none'
def _construct_id_verification(self, user): """ Helper function to create the SSO verified record for the user so that the user is ID Verified """ SSOVerificationFactory( identity_provider_slug=self.org_key_list[0], user=user, ) return IDVerificationService.user_status(user)
def test_denied_sso_verification(self): with freeze_time('2015-04-02'): # create denied sso verification for the user, make sure the denial # is handled properly SSOVerification.objects.create(user=self.user, status='denied') status = IDVerificationService.user_status(self.user) expected_status = { 'status': 'must_reverify', 'error': '', 'should_display': False, 'verification_expiry': '', 'status_date': '' } self.assertDictEqual(status, expected_status)
def test_search_username_user_no_enrollment(self, mocked_render): created_user, expected_user_info = self._construct_user( 'user_connected', self.org_key_list[0], self.external_user_key) self.client.get(self.url, data={'edx_user': created_user.email}) expected_info = { 'user': expected_user_info, 'id_verification': IDVerificationService.user_status(created_user), } render_call_dict = mocked_render.call_args[0][1] assert expected_info == render_call_dict['learner_program_enrollments']
def test_no_verification(self): with freeze_time('2014-12-12'): status = IDVerificationService.user_status(self.user) expected_status = { 'status': 'none', 'error': '', 'should_display': True, 'verification_expiry': '', 'status_date': '' } self.assertDictEqual(status, expected_status)
def test_user_status(self): # test for correct status when no error returned user = UserFactory.create() status = IDVerificationService.user_status(user) self.assertDictEqual(status, {'status': 'none', 'error': '', 'should_display': True}) # test for when photo verification has been created SoftwareSecurePhotoVerification.objects.create(user=user, status='approved') status = IDVerificationService.user_status(user) self.assertDictEqual(status, {'status': 'approved', 'error': '', 'should_display': True}) # create another photo verification for the same user, make sure the denial # is handled properly SoftwareSecurePhotoVerification.objects.create( user=user, status='denied', error_msg='[{"photoIdReasons": ["Not provided"]}]' ) status = IDVerificationService.user_status(user) self.assertDictEqual(status, {'status': 'must_reverify', 'error': ['id_image_missing'], 'should_display': True}) # test for when sso verification has been created SSOVerification.objects.create(user=user, status='approved') status = IDVerificationService.user_status(user) self.assertDictEqual(status, {'status': 'approved', 'error': '', 'should_display': False}) # create another sso verification for the same user, make sure the denial # is handled properly SSOVerification.objects.create(user=user, status='denied') status = IDVerificationService.user_status(user) self.assertDictEqual(status, {'status': 'must_reverify', 'error': '', 'should_display': False}) # test for when manual verification has been created ManualVerification.objects.create(user=user, status='approved') status = IDVerificationService.user_status(user) self.assertDictEqual(status, {'status': 'approved', 'error': '', 'should_display': False})
def verify_identity_url(self): """ Returns a String to the location to verify a learner's identity Note: This might return an absolute URL (if the verification MFE is enabled) or a relative URL. The serializer will make the relative URL absolute so any consumers can treat this as a full URL. """ if self.enrollment_object and self.enrollment_object.mode in CourseMode.VERIFIED_MODES: verification_status = IDVerificationService.user_status(self.effective_user)['status'] if verification_status == 'must_reverify': return IDVerificationService.get_verify_location() else: return IDVerificationService.get_verify_location(self.course_key)
def test_denied_software_secure_verification(self): with freeze_time('2015-2-02'): # create denied photo verification for the user, make sure the denial # is handled properly SoftwareSecurePhotoVerification.objects.create( user=self.user, status='denied', error_msg='[{"photoIdReasons": ["Not provided"]}]' ) status = IDVerificationService.user_status(self.user) expected_status = { 'status': 'must_reverify', 'error': ['id_image_missing'], 'should_display': True, 'verification_expiry': '', 'status_date': '', } self.assertDictEqual(status, expected_status)
def get(self, request): """ Handle the GET request. """ verification_status = IDVerificationService.user_status(request.user) expiration_datetime = IDVerificationService.get_expiration_datetime(request.user, ['approved']) can_verify = can_verify_now(verification_status, expiration_datetime) data = { 'status': verification_status['status'], 'can_verify': can_verify, } if expiration_datetime: data['expires'] = expiration_datetime return Response(data)
def test_expiring_software_secure_verification(self, new_status): with freeze_time('2015-07-11') as frozen_datetime: # create approved photo verification for the user SoftwareSecurePhotoVerification.objects.create(user=self.user, status='approved') expiring_datetime = datetime.now(utc) frozen_datetime.move_to('2015-07-14') # create another according to status passed in. SoftwareSecurePhotoVerification.objects.create(user=self.user, status=new_status) status_date = expiring_datetime if new_status == 'approved': status_date = datetime.now(utc) expected_status = {'status': 'approved', 'error': '', 'should_display': True, 'verification_expiry': '', 'status_date': status_date} status = IDVerificationService.user_status(self.user) self.assertDictEqual(status, expected_status)
def generate_certificate(self, **kwargs): """ Generates a certificate for a single user. kwargs: - student: The student for whom to generate a certificate. - course_key: The course key for the course that the student is receiving a certificate in. - expected_verification_status: The expected verification status for the user. When the status has changed, we double check that the actual verification status is as expected before generating a certificate, in the off chance that the database has not yet updated with the user's new verification status. - v2_certificate: A flag indicating whether to generate a v2 course certificate - generation_mode: Only used when emitting an event for V2 certificates. Options are "self" (implying the user generated the cert themself) and "batch" for everything else. """ original_kwargs = kwargs.copy() student = User.objects.get(id=kwargs.pop('student')) course_key = CourseKey.from_string(kwargs.pop('course_key')) expected_verification_status = kwargs.pop('expected_verification_status', None) v2_certificate = kwargs.pop('v2_certificate', False) status = kwargs.pop('status', CertificateStatuses.downloadable) generation_mode = kwargs.pop('generation_mode', 'batch') if v2_certificate: generate_course_certificate(user=student, course_key=course_key, status=status, generation_mode=generation_mode) return if expected_verification_status: actual_verification_status = IDVerificationService.user_status(student) actual_verification_status = actual_verification_status['status'] if expected_verification_status != actual_verification_status: log.warning('Expected verification status {expected} ' 'differs from actual verification status {actual} ' 'for user {user} in course {course}'.format( expected=expected_verification_status, actual=actual_verification_status, user=student.id, course=course_key)) raise self.retry(kwargs=original_kwargs) generate_user_certificates(student=student, course_key=course_key, **kwargs)
def generate_certificate(self, **kwargs): """ Generates a certificate for a single user. kwargs: - student: The student for whom to generate a certificate. - course_key: The course key for the course that the student is receiving a certificate in. - expected_verification_status: The expected verification status for the user. When the status has changed, we double check that the actual verification status is as expected before generating a certificate, in the off chance that the database has not yet updated with the user's new verification status. - allowlist_certificate: A flag indicating whether to generate an allowlist certificate (which is V2 of whitelisted certificates) - v2_certificate: A flag indicating whether to generate a v2 course certificate """ original_kwargs = kwargs.copy() student = User.objects.get(id=kwargs.pop('student')) course_key = CourseKey.from_string(kwargs.pop('course_key')) expected_verification_status = kwargs.pop('expected_verification_status', None) allowlist_certificate = kwargs.pop('allowlist_certificate', False) v2_certificate = kwargs.pop('v2_certificate', False) if allowlist_certificate: generate_allowlist_certificate(user=student, course_key=course_key) return if v2_certificate: # TODO: will be implemented in MICROBA-923 log.warning(f'Ignoring v2 certificate task request for {student.id}: {course_key}') return if expected_verification_status: actual_verification_status = IDVerificationService.user_status(student) actual_verification_status = actual_verification_status['status'] if expected_verification_status != actual_verification_status: log.warning( 'Expected verification status {expected} ' 'differs from actual verification status {actual} ' 'for user {user} in course {course}'.format( expected=expected_verification_status, actual=actual_verification_status, user=student.id, course=course_key )) raise self.retry(kwargs=original_kwargs) generate_user_certificates(student=student, course_key=course_key, **kwargs)
def test_override_verification(self, verification_type): with freeze_time('2015-07-11') as frozen_datetime: # create approved photo verification for the user SoftwareSecurePhotoVerification.objects.create(user=self.user, status='approved') frozen_datetime.move_to('2015-07-14') verification_type.objects.create(user=self.user, status='approved') expected_status = { 'status': 'approved', 'error': '', 'should_display': False, 'verification_expiry': '', 'status_date': datetime.now(utc) } status = IDVerificationService.user_status(self.user) self.assertDictEqual(status, expected_status)
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)
def get(self, request, **kwargs): # lint-amnesty, pylint: disable=missing-function-docstring username = kwargs.get('username') User = get_user_model() try: user = User.objects.get(username=username) user_status = IDVerificationService.user_status(user) if user_status.get('status') == 'none': raise Http404 return Response({ "is_verified": user_status.get('status') == 'approved', "status": user_status.get('status'), "expiration_datetime": user_status.get('verification_expiry', '') }) except User.DoesNotExist: raise Http404 # lint-amnesty, pylint: disable=raise-missing-from
def _get_external_user_info(self, external_user_key, org_key, idp_provider=None): """ Provided the external_user_key and org_key, return edx account info and program_enrollments_info if any. If we cannot identify the data, return empty object. """ found_user = None result = {} try: users_by_key = get_users_by_external_keys_and_org_key( [external_user_key], org_key) # Remove entries with no corresponding user and convert keys to lowercase users_by_key_lower = { key.lower(): value for key, value in users_by_key.items() if value } found_user = users_by_key_lower.get(external_user_key.lower()) except (BadOrganizationShortNameException, ProviderDoesNotExistException): # We cannot identify edX user from external_user_key and org_key pair pass enrollments = self._get_enrollments( external_user_key=external_user_key) if enrollments: result['enrollments'] = enrollments if found_user: user_social_auths = UserSocialAuth.objects.filter(user=found_user) if idp_provider: user_social_auths = user_social_auths.filter( provider=idp_provider.backend_name) user_info = serialize_user_info(found_user, user_social_auths) result['user'] = user_info result['id_verification'] = IDVerificationService.user_status( found_user) elif 'enrollments' in result: result['user'] = {'external_user_key': external_user_key} return result
def get(self, request, **kwargs): username = kwargs.get('username') User = get_user_model() try: user = User.objects.get(username=username) user_status = IDVerificationService.user_status(user) if user_status.get('status') == 'none': raise Http404 return Response({ "is_verified": user_status.get('status') == 'approved', "status": user_status.get('status'), "expiration_datetime": user_status.get('verification_expiry', '') }) except User.DoesNotExist: raise Http404
def test_override_verification(self, verification_type): with freeze_time('2015-07-11') as frozen_datetime: # create approved photo verification for the user SoftwareSecurePhotoVerification.objects.create( user=self.user, status='approved', expiration_date=now() + timedelta(days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"]) ) frozen_datetime.move_to('2015-07-14') verification_type.objects.create( user=self.user, status='approved', expiration_date=now() + timedelta(days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"]) ) expected_status = { 'status': 'approved', 'error': '', 'should_display': False, 'verification_expiry': '', 'status_date': now() } status = IDVerificationService.user_status(self.user) self.assertDictEqual(status, expected_status)
def get(self, request): """ Render the reverification flow. Most of the work is done client-side by composing the same Backbone views used in the initial verification flow. """ status, __ = IDVerificationService.user_status(request.user) expiration_datetime = IDVerificationService.get_expiration_datetime( request.user) can_reverify = False if expiration_datetime: if is_verification_expiring_soon(expiration_datetime): # The user has an active verification, but the verification # is set to expire within "EXPIRING_SOON_WINDOW" days (default is 4 weeks). # In this case user can resubmit photos for reverification. can_reverify = True # If the user has no initial verification or if the verification # process is still ongoing 'pending' or expired then allow the user to # submit the photo verification. # A photo verification is marked as 'pending' if its status is either # 'submitted' or 'must_retry'. if status in ["none", "must_reverify", "expired", "pending" ] or can_reverify: context = { "user_full_name": request.user.profile.name, "platform_name": configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), "capture_sound": staticfiles_storage.url("audio/camera_capture.wav"), } return render_to_response("verify_student/reverify.html", context) else: context = {"status": status} return render_to_response( "verify_student/reverify_not_allowed.html", context)
def get(self, request): """ Render the reverification flow. Most of the work is done client-side by composing the same Backbone views used in the initial verification flow. """ verification_status = IDVerificationService.user_status(request.user) expiration_datetime = IDVerificationService.get_expiration_datetime(request.user, ['approved']) if can_verify_now(verification_status, expiration_datetime): context = { "user_full_name": request.user.profile.name, "platform_name": configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), "capture_sound": staticfiles_storage.url("audio/camera_capture.wav"), } return render_to_response("verify_student/reverify.html", context) else: context = { "status": verification_status['status'] } return render_to_response("verify_student/reverify_not_allowed.html", context)
def get(self, request): """ Render the reverification flow. Most of the work is done client-side by composing the same Backbone views used in the initial verification flow. """ verification_status = IDVerificationService.user_status(request.user) expiration_datetime = IDVerificationService.get_expiration_datetime(request.user, ['approved']) can_reverify = False if expiration_datetime: if is_verification_expiring_soon(expiration_datetime): # The user has an active verification, but the verification # is set to expire within "EXPIRING_SOON_WINDOW" days (default is 4 weeks). # In this case user can resubmit photos for reverification. can_reverify = True # If the user has no initial verification or if the verification # process is still ongoing 'pending' or expired then allow the user to # submit the photo verification. # A photo verification is marked as 'pending' if its status is either # 'submitted' or 'must_retry'. if verification_status['status'] in ["none", "must_reverify", "expired", "pending"] or can_reverify: context = { "user_full_name": request.user.profile.name, "platform_name": configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), "capture_sound": staticfiles_storage.url("audio/camera_capture.wav"), } return render_to_response("verify_student/reverify.html", context) else: context = { "status": verification_status['status'] } return render_to_response("verify_student/reverify_not_allowed.html", context)
def verification_status(self): """Return the verification status for this user.""" verification_status = IDVerificationService.user_status(self.user) return verification_status['status']
def student_dashboard(request): """ Provides the LMS dashboard view TODO: This is lms specific and does not belong in common code. Arguments: request: The request object. Returns: The dashboard response. """ user = request.user if not UserProfile.objects.filter(user=user).exists(): return redirect(reverse('account_settings')) platform_name = configuration_helpers.get_value("platform_name", settings.PLATFORM_NAME) enable_verified_certificates = configuration_helpers.get_value( 'ENABLE_VERIFIED_CERTIFICATES', settings.FEATURES.get('ENABLE_VERIFIED_CERTIFICATES') ) display_course_modes_on_dashboard = configuration_helpers.get_value( 'DISPLAY_COURSE_MODES_ON_DASHBOARD', settings.FEATURES.get('DISPLAY_COURSE_MODES_ON_DASHBOARD', True) ) activation_email_support_link = configuration_helpers.get_value( 'ACTIVATION_EMAIL_SUPPORT_LINK', settings.ACTIVATION_EMAIL_SUPPORT_LINK ) or settings.SUPPORT_SITE_LINK # Get the org whitelist or the org blacklist for the current site site_org_whitelist, site_org_blacklist = get_org_black_and_whitelist_for_site() course_enrollments = list(get_course_enrollments(user, site_org_whitelist, site_org_blacklist)) # Get the entitlements for the user and a mapping to all available sessions for that entitlement # If an entitlement has no available sessions, pass through a mock course overview object (course_entitlements, course_entitlement_available_sessions, unfulfilled_entitlement_pseudo_sessions) = get_filtered_course_entitlements( user, site_org_whitelist, site_org_blacklist ) # Record how many courses there are so that we can get a better # understanding of usage patterns on prod. monitoring_utils.accumulate('num_courses', len(course_enrollments)) # Sort the enrollment pairs by the enrollment date course_enrollments.sort(key=lambda x: x.created, reverse=True) # Retrieve the course modes for each course enrolled_course_ids = [enrollment.course_id for enrollment in course_enrollments] __, unexpired_course_modes = CourseMode.all_and_unexpired_modes_for_courses(enrolled_course_ids) course_modes_by_course = { course_id: { mode.slug: mode for mode in modes } for course_id, modes in iteritems(unexpired_course_modes) } # Check to see if the student has recently enrolled in a course. # If so, display a notification message confirming the enrollment. enrollment_message = _create_recent_enrollment_message( course_enrollments, course_modes_by_course ) course_optouts = Optout.objects.filter(user=user).values_list('course_id', flat=True) sidebar_account_activation_message = '' banner_account_activation_message = '' display_account_activation_message_on_sidebar = configuration_helpers.get_value( 'DISPLAY_ACCOUNT_ACTIVATION_MESSAGE_ON_SIDEBAR', settings.FEATURES.get('DISPLAY_ACCOUNT_ACTIVATION_MESSAGE_ON_SIDEBAR', False) ) # Display activation message in sidebar if DISPLAY_ACCOUNT_ACTIVATION_MESSAGE_ON_SIDEBAR # flag is active. Otherwise display existing message at the top. if display_account_activation_message_on_sidebar and not user.is_active: sidebar_account_activation_message = render_to_string( 'registration/account_activation_sidebar_notice.html', { 'email': user.email, 'platform_name': platform_name, 'activation_email_support_link': activation_email_support_link } ) elif not user.is_active: banner_account_activation_message = render_to_string( 'registration/activate_account_notice.html', {'email': user.email} ) enterprise_message = get_dashboard_consent_notification(request, user, course_enrollments) # Disable lookup of Enterprise consent_required_course due to ENT-727 # Will re-enable after fixing WL-1315 consent_required_courses = set() enterprise_customer_name = None # Account activation message account_activation_messages = [ message for message in messages.get_messages(request) if 'account-activation' in message.tags ] # Global staff can see what courses encountered an error on their dashboard staff_access = False errored_courses = {} if has_access(user, 'staff', 'global'): # Show any courses that encountered an error on load staff_access = True errored_courses = modulestore().get_errored_courses() show_courseware_links_for = frozenset( enrollment.course_id for enrollment in course_enrollments if has_access(request.user, 'load', enrollment.course_overview) ) # Find programs associated with course runs being displayed. This information # is passed in the template context to allow rendering of program-related # information on the dashboard. meter = ProgramProgressMeter(request.site, user, enrollments=course_enrollments) ecommerce_service = EcommerceService() inverted_programs = meter.invert_programs() urls, programs_data = {}, {} bundles_on_dashboard_flag = WaffleFlag(WaffleFlagNamespace(name=u'student.experiments'), u'bundles_on_dashboard') # TODO: Delete this code and the relevant HTML code after testing LEARNER-3072 is complete if bundles_on_dashboard_flag.is_enabled() and inverted_programs and inverted_programs.items(): if len(course_enrollments) < 4: for program in inverted_programs.values(): try: program_uuid = program[0]['uuid'] program_data = get_programs(request.site, uuid=program_uuid) program_data = ProgramDataExtender(program_data, request.user).extend() skus = program_data.get('skus') checkout_page_url = ecommerce_service.get_checkout_page_url(*skus) program_data['completeProgramURL'] = checkout_page_url + '&bundle=' + program_data.get('uuid') programs_data[program_uuid] = program_data except: # pylint: disable=bare-except pass # Construct a dictionary of course mode information # used to render the course list. We re-use the course modes dict # we loaded earlier to avoid hitting the database. course_mode_info = { enrollment.course_id: complete_course_mode_info( enrollment.course_id, enrollment, modes=course_modes_by_course[enrollment.course_id] ) for enrollment in course_enrollments } # Determine the per-course verification status # This is a dictionary in which the keys are course locators # and the values are one of: # # VERIFY_STATUS_NEED_TO_VERIFY # VERIFY_STATUS_SUBMITTED # VERIFY_STATUS_APPROVED # VERIFY_STATUS_MISSED_DEADLINE # # Each of which correspond to a particular message to display # next to the course on the dashboard. # # If a course is not included in this dictionary, # there is no verification messaging to display. verify_status_by_course = check_verify_status_by_course(user, course_enrollments) cert_statuses = { enrollment.course_id: cert_info(request.user, enrollment.course_overview) for enrollment in course_enrollments } # only show email settings for Mongo course and when bulk email is turned on show_email_settings_for = frozenset( enrollment.course_id for enrollment in course_enrollments if ( BulkEmailFlag.feature_enabled(enrollment.course_id) ) ) # Verification Attempts # Used to generate the "you must reverify for course x" banner verification_status = IDVerificationService.user_status(user) verification_errors = get_verification_error_reasons_for_display(verification_status['error']) # Gets data for midcourse reverifications, if any are necessary or have failed statuses = ["approved", "denied", "pending", "must_reverify"] reverifications = reverification_info(statuses) block_courses = frozenset( enrollment.course_id for enrollment in course_enrollments if is_course_blocked( request, CourseRegistrationCode.objects.filter( course_id=enrollment.course_id, registrationcoderedemption__redeemed_by=request.user ), enrollment.course_id ) ) enrolled_courses_either_paid = frozenset( enrollment.course_id for enrollment in course_enrollments if enrollment.is_paid_course() ) # If there are *any* denied reverifications that have not been toggled off, # we'll display the banner denied_banner = any(item.display for item in reverifications["denied"]) # Populate the Order History for the side-bar. order_history_list = order_history( user, course_org_filter=site_org_whitelist, org_filter_out_set=site_org_blacklist ) # get list of courses having pre-requisites yet to be completed courses_having_prerequisites = frozenset( enrollment.course_id for enrollment in course_enrollments if enrollment.course_overview.pre_requisite_courses ) courses_requirements_not_met = get_pre_requisite_courses_not_completed(user, courses_having_prerequisites) if 'notlive' in request.GET: redirect_message = _("The course you are looking for does not start until {date}.").format( date=request.GET['notlive'] ) elif 'course_closed' in request.GET: redirect_message = _("The course you are looking for is closed for enrollment as of {date}.").format( date=request.GET['course_closed'] ) else: redirect_message = '' valid_verification_statuses = ['approved', 'must_reverify', 'pending', 'expired'] display_sidebar_on_dashboard = (len(order_history_list) or (verification_status['status'] in valid_verification_statuses and verification_status['should_display'])) # Filter out any course enrollment course cards that are associated with fulfilled entitlements for entitlement in [e for e in course_entitlements if e.enrollment_course_run is not None]: course_enrollments = [ enr for enr in course_enrollments if entitlement.enrollment_course_run.course_id != enr.course_id ] context = { 'urls': urls, 'programs_data': programs_data, 'enterprise_message': enterprise_message, 'consent_required_courses': consent_required_courses, 'enterprise_customer_name': enterprise_customer_name, 'enrollment_message': enrollment_message, 'redirect_message': redirect_message, 'account_activation_messages': account_activation_messages, 'course_enrollments': course_enrollments, 'course_entitlements': course_entitlements, 'course_entitlement_available_sessions': course_entitlement_available_sessions, 'unfulfilled_entitlement_pseudo_sessions': unfulfilled_entitlement_pseudo_sessions, 'course_optouts': course_optouts, 'banner_account_activation_message': banner_account_activation_message, 'sidebar_account_activation_message': sidebar_account_activation_message, 'staff_access': staff_access, 'errored_courses': errored_courses, 'show_courseware_links_for': show_courseware_links_for, 'all_course_modes': course_mode_info, 'cert_statuses': cert_statuses, 'credit_statuses': _credit_statuses(user, course_enrollments), 'show_email_settings_for': show_email_settings_for, 'reverifications': reverifications, 'verification_display': verification_status['should_display'], 'verification_status': verification_status['status'], 'verification_status_by_course': verify_status_by_course, 'verification_errors': verification_errors, 'block_courses': block_courses, 'denied_banner': denied_banner, 'billing_email': settings.PAYMENT_SUPPORT_EMAIL, 'user': user, 'logout_url': reverse('logout'), 'platform_name': platform_name, 'enrolled_courses_either_paid': enrolled_courses_either_paid, 'provider_states': [], 'order_history_list': order_history_list, 'courses_requirements_not_met': courses_requirements_not_met, 'nav_hidden': True, 'inverted_programs': inverted_programs, 'show_program_listing': ProgramsApiConfig.is_enabled(), 'show_dashboard_tabs': True, 'disable_courseware_js': True, 'display_course_modes_on_dashboard': enable_verified_certificates and display_course_modes_on_dashboard, 'display_sidebar_on_dashboard': display_sidebar_on_dashboard, } if ecommerce_service.is_enabled(request.user): context.update({ 'use_ecommerce_payment_flow': True, 'ecommerce_payment_page': ecommerce_service.payment_page_url(), }) # Gather urls for course card resume buttons. resume_button_urls = _get_urls_for_resume_buttons(user, course_enrollments) # There must be enough urls for dashboard.html. Template creates course # cards for "enrollments + entitlements". resume_button_urls += ['' for entitlement in course_entitlements] context.update({ 'resume_button_urls': resume_button_urls }) response = render_to_response('dashboard.html', context) set_user_info_cookie(response, request) return response