diff --git a/lms/djangoapps/utils.py b/lms/djangoapps/utils.py index 7a8e9cbea9..e034b6ed51 100644 --- a/lms/djangoapps/utils.py +++ b/lms/djangoapps/utils.py @@ -1,7 +1,6 @@ """ Helper Methods """ -import six def _get_key(key_or_id, key_cls): @@ -11,6 +10,6 @@ def _get_key(key_or_id, key_cls): """ return ( key_cls.from_string(key_or_id) - if isinstance(key_or_id, six.string_types) + if isinstance(key_or_id, str) else key_or_id ) diff --git a/lms/djangoapps/verify_student/admin.py b/lms/djangoapps/verify_student/admin.py index b4ea2aae1b..6de066e6f7 100644 --- a/lms/djangoapps/verify_student/admin.py +++ b/lms/djangoapps/verify_student/admin.py @@ -1,4 +1,3 @@ -# encoding: utf-8 """ Admin site configurations for verify_student. """ @@ -7,8 +6,11 @@ Admin site configurations for verify_student. from django.contrib import admin from lms.djangoapps.verify_student.models import ( - ManualVerification, SoftwareSecurePhotoVerification, SSOVerification, - SSPVerificationRetryConfig) + ManualVerification, + SoftwareSecurePhotoVerification, + SSOVerification, + SSPVerificationRetryConfig +) @admin.register(SoftwareSecurePhotoVerification) diff --git a/lms/djangoapps/verify_student/management/commands/backfill_sso_verifications_for_old_account_links.py b/lms/djangoapps/verify_student/management/commands/backfill_sso_verifications_for_old_account_links.py index 16e6fc7aae..e50c67d11d 100644 --- a/lms/djangoapps/verify_student/management/commands/backfill_sso_verifications_for_old_account_links.py +++ b/lms/djangoapps/verify_student/management/commands/backfill_sso_verifications_for_old_account_links.py @@ -12,12 +12,11 @@ of SSO IDV records. """ from django.core.management.base import BaseCommand, CommandError - from social_django.models import UserSocialAuth from common.djangoapps.third_party_auth.api.utils import filter_user_social_auth_queryset_by_provider -from lms.djangoapps.verify_student.models import SSOVerification from common.djangoapps.third_party_auth.provider import Registry +from lms.djangoapps.verify_student.models import SSOVerification class Command(BaseCommand): @@ -50,7 +49,7 @@ class Command(BaseCommand): try: provider = Registry.get(provider_slug) except ValueError as e: # lint-amnesty, pylint: disable=unused-variable - raise CommandError('provider slug {slug} does not exist'.format(slug=provider_slug)) # lint-amnesty, pylint: disable=raise-missing-from + raise CommandError(f'provider slug {provider_slug} does not exist') # lint-amnesty, pylint: disable=raise-missing-from query_set = UserSocialAuth.objects.select_related('user__profile') query_set = filter_user_social_auth_queryset_by_provider(query_set, provider) diff --git a/lms/djangoapps/verify_student/management/commands/manual_verifications.py b/lms/djangoapps/verify_student/management/commands/manual_verifications.py index 4205fc1f17..5cc3a78ea7 100644 --- a/lms/djangoapps/verify_student/management/commands/manual_verifications.py +++ b/lms/djangoapps/verify_student/management/commands/manual_verifications.py @@ -41,18 +41,18 @@ class Command(BaseCommand): if email_ids_file: if not os.path.exists(email_ids_file): - raise CommandError(u'Pass the correct absolute path to email ids file as --email-ids-file argument.') + raise CommandError('Pass the correct absolute path to email ids file as --email-ids-file argument.') total_emails, failed_emails = self._generate_manual_verification_from_file(email_ids_file) if failed_emails: - log.error(u'Completed manual verification. {} of {} failed.'.format( + log.error('Completed manual verification. {} of {} failed.'.format( len(failed_emails), total_emails )) - log.error(u'Failed emails:{}'.format(pformat(failed_emails))) + log.error('Failed emails:{}'.format(pformat(failed_emails))) else: - log.info(u'Successfully generated manual verification for {} emails.'.format(total_emails)) + log.info(f'Successfully generated manual verification for {total_emails} emails.') def _generate_manual_verification_from_file(self, email_ids_file): """ @@ -67,10 +67,10 @@ class Command(BaseCommand): """ failed_emails = [] - with open(email_ids_file, 'r') as file_handler: + with open(email_ids_file) as file_handler: email_ids = file_handler.readlines() total_emails = len(email_ids) - log.info(u'Creating manual verification for {} emails.'.format(total_emails)) + log.info(f'Creating manual verification for {total_emails} emails.') for email_id in email_ids: try: email_id = email_id.strip() @@ -83,6 +83,6 @@ class Command(BaseCommand): ) except User.DoesNotExist: failed_emails.append(email_id) - err_msg = u'Tried to verify email {}, but user not found' + err_msg = 'Tried to verify email {}, but user not found' log.error(err_msg.format(email_id)) return total_emails, failed_emails diff --git a/lms/djangoapps/verify_student/management/commands/populate_expiry_date.py b/lms/djangoapps/verify_student/management/commands/populate_expiry_date.py index cc4317fa76..9d2da9f09b 100644 --- a/lms/djangoapps/verify_student/management/commands/populate_expiry_date.py +++ b/lms/djangoapps/verify_student/management/commands/populate_expiry_date.py @@ -12,8 +12,8 @@ from django.conf import settings from django.core.management.base import BaseCommand from django.db.models import F -from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification from common.djangoapps.util.query import use_read_replica_if_available +from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification logger = logging.getLogger(__name__) diff --git a/lms/djangoapps/verify_student/management/commands/retry_failed_photo_verifications.py b/lms/djangoapps/verify_student/management/commands/retry_failed_photo_verifications.py index 352b23cb15..cf9aaf43d7 100644 --- a/lms/djangoapps/verify_student/management/commands/retry_failed_photo_verifications.py +++ b/lms/djangoapps/verify_student/management/commands/retry_failed_photo_verifications.py @@ -4,8 +4,9 @@ Django admin commands related to verify_student import logging -from django.core.management.base import BaseCommand + from django.core.management.base import CommandError # lint-amnesty, pylint: disable=unused-import +from django.core.management.base import BaseCommand from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, SSPVerificationRetryConfig @@ -64,20 +65,20 @@ class Command(BaseCommand): attempts_to_retry = SoftwareSecurePhotoVerification.objects.filter( receipt_id__in=options['verification_ids'] ) - log.info(u"Fetching retry verification ids from config model") + log.info("Fetching retry verification ids from config model") force_must_retry = True else: attempts_to_retry = SoftwareSecurePhotoVerification.objects.filter(status='must_retry') force_must_retry = False - log.info(u"Attempting to retry {0} failed PhotoVerification submissions".format(len(attempts_to_retry))) + log.info("Attempting to retry {} failed PhotoVerification submissions".format(len(attempts_to_retry))) for index, attempt in enumerate(attempts_to_retry): - log.info(u"Retrying submission #{0} (ID: {1}, User: {2})".format(index, attempt.id, attempt.user)) + log.info(f"Retrying submission #{index} (ID: {attempt.id}, User: {attempt.user})") # Set the attempts status to 'must_retry' so that we can re-submit it if force_must_retry: attempt.status = 'must_retry' attempt.submit(copy_id_photo_from=attempt.copy_id_photo_from) - log.info(u"Retry result: {0}".format(attempt.status)) + log.info(f"Retry result: {attempt.status}") log.info("Done resubmitting failed photo verifications") diff --git a/lms/djangoapps/verify_student/management/commands/send_verification_expiry_email.py b/lms/djangoapps/verify_student/management/commands/send_verification_expiry_email.py index 89fbe3782b..137af10a9a 100644 --- a/lms/djangoapps/verify_student/management/commands/send_verification_expiry_email.py +++ b/lms/djangoapps/verify_student/management/commands/send_verification_expiry_email.py @@ -7,19 +7,19 @@ import logging import time from datetime import timedelta -from common.djangoapps.course_modes.models import CourseMode -from django.conf import settings # lint-amnesty, pylint: disable=wrong-import-order +from django.conf import settings from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user, wrong-import-order -from django.contrib.sites.models import Site # lint-amnesty, pylint: disable=wrong-import-order -from django.core.management.base import BaseCommand, CommandError # lint-amnesty, pylint: disable=wrong-import-order -from django.db.models import Q # lint-amnesty, pylint: disable=wrong-import-order +from django.contrib.sites.models import Site +from django.core.management.base import BaseCommand, CommandError +from django.db.models import Q from django.urls import reverse # lint-amnesty, pylint: disable=unused-import, wrong-import-order -from django.utils.timezone import now # lint-amnesty, pylint: disable=wrong-import-order -from edx_ace import ace # lint-amnesty, pylint: disable=wrong-import-order -from edx_ace.recipient import Recipient # lint-amnesty, pylint: disable=wrong-import-order +from django.utils.timezone import now +from edx_ace import ace +from edx_ace.recipient import Recipient + +from common.djangoapps.course_modes.models import CourseMode from common.djangoapps.student.models import CourseEnrollment from common.djangoapps.util.query import use_read_replica_if_available - from lms.djangoapps.verify_student.message_types import VerificationExpiry from lms.djangoapps.verify_student.models import ManualVerification, SoftwareSecurePhotoVerification, SSOVerification from lms.djangoapps.verify_student.services import IDVerificationService # lint-amnesty, pylint: disable=unused-import @@ -91,8 +91,8 @@ class Command(BaseCommand): dry_run = options['dry_run'] if default_emails <= 0: - raise CommandError(u'DEFAULT_EMAILS must be a positive integer. If you do not wish to send emails ' - u'use --dry-run flag instead.') + raise CommandError('DEFAULT_EMAILS must be a positive integer. If you do not wish to send emails ' + 'use --dry-run flag instead.') end_date = now().replace(hour=0, minute=0, second=0, microsecond=0) # If email was sent and user did not re-verify then this date will be used as the criteria for resending email @@ -121,11 +121,11 @@ class Command(BaseCommand): total_verification = sspv.count() if not total_verification: - logger.info(u"No approved expired entries found in SoftwareSecurePhotoVerification for the " - u"date range {} - {}".format(start_date.date(), now().date())) + logger.info("No approved expired entries found in SoftwareSecurePhotoVerification for the " + "date range {} - {}".format(start_date.date(), now().date())) return - logger.info(u"For the date range {} - {}, total Software Secure Photo verification filtered are {}" + logger.info("For the date range {} - {}, total Software Secure Photo verification filtered are {}" .format(start_date.date(), now().date(), total_verification)) batch_verifications = [] @@ -191,8 +191,8 @@ class Command(BaseCommand): """ if email_config['dry_run']: logger.info( - u"This was a dry run, no email was sent. For the actual run email would have been sent " - u"to {} learner(s)".format(len(batch_verifications)) + "This was a dry run, no email was sent. For the actual run email would have been sent " + "to {} learner(s)".format(len(batch_verifications)) ) return True @@ -200,7 +200,7 @@ class Command(BaseCommand): message_context = get_base_template_context(site) message_context.update({ 'platform_name': settings.PLATFORM_NAME, - 'lms_verification_link': '{}/id-verification'.format(settings.ACCOUNT_MICROFRONTEND_URL), + 'lms_verification_link': f'{settings.ACCOUNT_MICROFRONTEND_URL}/id-verification', 'help_center_link': settings.ID_VERIFICATION_SUPPORT_LINK }) diff --git a/lms/djangoapps/verify_student/management/commands/tests/test_backfill_sso_verifications_for_old_account_links.py b/lms/djangoapps/verify_student/management/commands/tests/test_backfill_sso_verifications_for_old_account_links.py index 57d163c6ef..4a93aa19f1 100644 --- a/lms/djangoapps/verify_student/management/commands/tests/test_backfill_sso_verifications_for_old_account_links.py +++ b/lms/djangoapps/verify_student/management/commands/tests/test_backfill_sso_verifications_for_old_account_links.py @@ -2,15 +2,16 @@ Tests for management command backfill_sso_verifications_for_old_account_links """ -from mock import patch +from unittest.mock import patch + import pytest from django.core.management import call_command from django.core.management.base import CommandError +from common.djangoapps.third_party_auth.tests.testutil import TestCase from lms.djangoapps.program_enrollments.management.commands.tests.utils import UserSocialAuthFactory from lms.djangoapps.verify_student.models import SSOVerification from lms.djangoapps.verify_student.tests.factories import SSOVerificationFactory -from common.djangoapps.third_party_auth.tests.testutil import TestCase class TestBackfillSSOVerificationsCommand(TestCase): @@ -20,7 +21,7 @@ class TestBackfillSSOVerificationsCommand(TestCase): slug = 'test' def setUp(self): - super(TestBackfillSSOVerificationsCommand, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.enable_saml() self.provider = self.configure_saml_provider( name="Test", diff --git a/lms/djangoapps/verify_student/management/commands/tests/test_manual_verify_student.py b/lms/djangoapps/verify_student/management/commands/tests/test_manual_verify_student.py index f3a1ee9229..b6dd092267 100644 --- a/lms/djangoapps/verify_student/management/commands/tests/test_manual_verify_student.py +++ b/lms/djangoapps/verify_student/management/commands/tests/test_manual_verify_student.py @@ -7,15 +7,15 @@ Tests for django admin commands in the verify_student module import logging import os import tempfile + import pytest -import six from django.core.management import CommandError, call_command from django.test import TestCase from testfixtures import LogCapture +from common.djangoapps.student.tests.factories import UserFactory from lms.djangoapps.verify_student.models import ManualVerification from lms.djangoapps.verify_student.utils import earliest_allowed_verification_date -from common.djangoapps.student.tests.factories import UserFactory LOGGER_NAME = 'lms.djangoapps.verify_student.management.commands.manual_verifications' @@ -27,11 +27,11 @@ class TestVerifyStudentCommand(TestCase): tmp_file_path = os.path.join(tempfile.gettempdir(), 'tmp-emails.txt') def setUp(self): - super(TestVerifyStudentCommand, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user1 = UserFactory.create() self.user2 = UserFactory.create() self.user3 = UserFactory.create() - self.invalid_email = six.text_type('unknown@unknown.com') + self.invalid_email = 'unknown@unknown.com' self.create_email_ids_file( self.tmp_file_path, @@ -87,15 +87,15 @@ class TestVerifyStudentCommand(TestCase): expected_log = ( (LOGGER_NAME, 'INFO', - u'Creating manual verification for 4 emails.' + 'Creating manual verification for 4 emails.' ), (LOGGER_NAME, 'ERROR', - u'Tried to verify email unknown@unknown.com, but user not found' + 'Tried to verify email unknown@unknown.com, but user not found' ), (LOGGER_NAME, 'ERROR', - u'Completed manual verification. 1 of 4 failed.' + 'Completed manual verification. 1 of 4 failed.' ), (LOGGER_NAME, 'ERROR', diff --git a/lms/djangoapps/verify_student/management/commands/tests/test_populate_expiry_date.py b/lms/djangoapps/verify_student/management/commands/tests/test_populate_expiry_date.py index 5e4b5954a8..30751224e6 100644 --- a/lms/djangoapps/verify_student/management/commands/tests/test_populate_expiry_date.py +++ b/lms/djangoapps/verify_student/management/commands/tests/test_populate_expiry_date.py @@ -4,17 +4,17 @@ Tests for django admin command `populate_expiry_date` in the verify_student modu from datetime import timedelta +from unittest.mock import patch from django.conf import settings from django.core.management import call_command from django.test import TestCase -from mock import patch from testfixtures import LogCapture +from common.djangoapps.student.tests.factories import UserFactory from common.test.utils import MockS3BotoMixin from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification from lms.djangoapps.verify_student.tests.test_models import FAKE_SETTINGS, mock_software_secure_post -from common.djangoapps.student.tests.factories import UserFactory LOGGER_NAME = 'lms.djangoapps.verify_student.management.commands.populate_expiry_date' diff --git a/lms/djangoapps/verify_student/management/commands/tests/test_send_verification_expiry_email.py b/lms/djangoapps/verify_student/management/commands/tests/test_send_verification_expiry_email.py index 7a6440d543..8a821b775d 100644 --- a/lms/djangoapps/verify_student/management/commands/tests/test_send_verification_expiry_email.py +++ b/lms/djangoapps/verify_student/management/commands/tests/test_send_verification_expiry_email.py @@ -4,18 +4,18 @@ Tests for django admin command `send_verification_expiry_email` in the verify_st from datetime import timedelta +from unittest.mock import patch from django.conf import settings from django.contrib.sites.models import Site from django.core import mail -from django.core.management import call_command, CommandError +from django.core.management import CommandError, call_command from django.test import TestCase from django.test.utils import override_settings from django.utils.timezone import now -from mock import patch -from common.djangoapps.student.tests.factories import UserFactory -from testfixtures import LogCapture # lint-amnesty, pylint: disable=wrong-import-order +from testfixtures import LogCapture +from common.djangoapps.student.tests.factories import UserFactory from common.test.utils import MockS3BotoMixin from lms.djangoapps.verify_student.models import ManualVerification, SoftwareSecurePhotoVerification, SSOVerification from lms.djangoapps.verify_student.tests.test_models import FAKE_SETTINGS, mock_software_secure_post @@ -30,7 +30,7 @@ class TestSendVerificationExpiryEmail(MockS3BotoMixin, TestCase): def setUp(self): """ Initial set up for tests """ - super(TestSendVerificationExpiryEmail, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() Site.objects.create(domain='edx.org', name='edx.org') self.resend_days = settings.VERIFICATION_EXPIRY_EMAIL['RESEND_DAYS'] self.days = settings.VERIFICATION_EXPIRY_EMAIL['DAYS_RANGE'] @@ -184,8 +184,8 @@ class TestSendVerificationExpiryEmail(MockS3BotoMixin, TestCase): call_command('send_verification_expiry_email') logger.check( (LOGGER_NAME, - 'INFO', u"No approved expired entries found in SoftwareSecurePhotoVerification for the " - u"date range {} - {}".format(start_date.date(), now().date())) + 'INFO', "No approved expired entries found in SoftwareSecurePhotoVerification for the " + "date range {} - {}".format(start_date.date(), now().date())) ) def test_dry_run_flag(self): @@ -206,13 +206,13 @@ class TestSendVerificationExpiryEmail(MockS3BotoMixin, TestCase): logger.check( (LOGGER_NAME, 'INFO', - u"For the date range {} - {}, total Software Secure Photo verification filtered are {}" + "For the date range {} - {}, total Software Secure Photo verification filtered are {}" .format(start_date.date(), now().date(), count) ), (LOGGER_NAME, 'INFO', - u"This was a dry run, no email was sent. For the actual run email would have been sent " - u"to {} learner(s)".format(count) + "This was a dry run, no email was sent. For the actual run email would have been sent " + "to {} learner(s)".format(count) )) assert len(mail.outbox) == 0 @@ -269,8 +269,8 @@ class TestSendVerificationExpiryEmail(MockS3BotoMixin, TestCase): @override_settings(VERIFICATION_EXPIRY_EMAIL={'RESEND_DAYS': 15, 'DAYS_RANGE': 1, 'DEFAULT_EMAILS': 0}) def test_command_error(self): - err_string = u"DEFAULT_EMAILS must be a positive integer. If you do not wish to send " \ - u"emails use --dry-run flag instead." + err_string = "DEFAULT_EMAILS must be a positive integer. If you do not wish to send " \ + "emails use --dry-run flag instead." with self.assertRaisesRegex(CommandError, err_string): call_command('send_verification_expiry_email') diff --git a/lms/djangoapps/verify_student/management/commands/tests/test_update_expiration_date.py b/lms/djangoapps/verify_student/management/commands/tests/test_update_expiration_date.py index be2c41c5a5..93ae9dfb5b 100644 --- a/lms/djangoapps/verify_student/management/commands/tests/test_update_expiration_date.py +++ b/lms/djangoapps/verify_student/management/commands/tests/test_update_expiration_date.py @@ -4,18 +4,18 @@ Tests for django admin command `update_expiration_date` in the verify_student mo from datetime import timedelta +from unittest.mock import patch from django.conf import settings from django.core.management import call_command from django.test import TestCase from django.utils.timezone import now -from mock import patch from testfixtures import LogCapture +from common.djangoapps.student.tests.factories import UserFactory from common.test.utils import MockS3BotoMixin from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification from lms.djangoapps.verify_student.tests.test_models import FAKE_SETTINGS, mock_software_secure_post -from common.djangoapps.student.tests.factories import UserFactory LOGGER_NAME = 'lms.djangoapps.verify_student.management.commands.update_expiration_date' diff --git a/lms/djangoapps/verify_student/management/commands/tests/test_verify_student.py b/lms/djangoapps/verify_student/management/commands/tests/test_verify_student.py index 2f74127aa8..850100d757 100644 --- a/lms/djangoapps/verify_student/management/commands/tests/test_verify_student.py +++ b/lms/djangoapps/verify_student/management/commands/tests/test_verify_student.py @@ -4,11 +4,14 @@ Tests for django admin commands in the verify_student module Lots of imports from verify_student's model tests, since they cover similar ground """ +from unittest.mock import patch + from django.conf import settings from django.core.management import call_command -from mock import patch from testfixtures import LogCapture +from common.djangoapps.student.tests.factories import \ + UserFactory # lint-amnesty, pylint: disable=import-error, unused-import, useless-suppression from common.test.utils import MockS3BotoMixin from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, SSPVerificationRetryConfig from lms.djangoapps.verify_student.tests import TestVerificationBase @@ -17,7 +20,6 @@ from lms.djangoapps.verify_student.tests.test_models import ( mock_software_secure_post, mock_software_secure_post_error ) -from common.djangoapps.student.tests.factories import UserFactory # lint-amnesty, pylint: disable=import-error, unused-import, useless-suppression LOGGER_NAME = 'retry_photo_verification' @@ -77,7 +79,7 @@ class TestVerifyStudentCommand(MockS3BotoMixin, TestVerificationBase): log.check_present( ( LOGGER_NAME, 'INFO', - 'Attempting to retry {0} failed PhotoVerification submissions'.format(1) + 'Attempting to retry {} failed PhotoVerification submissions'.format(1) ), ) diff --git a/lms/djangoapps/verify_student/management/commands/update_expiration_date.py b/lms/djangoapps/verify_student/management/commands/update_expiration_date.py index c669a3dcb0..24506a6b9e 100644 --- a/lms/djangoapps/verify_student/management/commands/update_expiration_date.py +++ b/lms/djangoapps/verify_student/management/commands/update_expiration_date.py @@ -11,8 +11,8 @@ from django.conf import settings # lint-amnesty, pylint: disable=unused-import from django.core.management.base import BaseCommand from django.db.models import F -from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification from common.djangoapps.util.query import use_read_replica_if_available +from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification logger = logging.getLogger(__name__) diff --git a/lms/djangoapps/verify_student/message_types.py b/lms/djangoapps/verify_student/message_types.py index 7d4b80dee7..6401e1a805 100644 --- a/lms/djangoapps/verify_student/message_types.py +++ b/lms/djangoapps/verify_student/message_types.py @@ -9,7 +9,7 @@ class VerificationExpiry(BaseMessageType): # lint-amnesty, pylint: disable=miss Name = 'verificationexpiry' def __init__(self, *args, **kwargs): - super(VerificationExpiry, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.options['transactional'] = True @@ -22,7 +22,7 @@ class VerificationApproved(BaseMessageType): Name = 'verificationapproved' def __init__(self, *args, **kwargs): - super(VerificationApproved, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.options['transactional'] = True @@ -34,5 +34,5 @@ class VerificationSubmitted(BaseMessageType): Name = 'verificationsubmitted' def __init__(self, *args, **kwargs): - super(VerificationSubmitted, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.options['transactional'] = True diff --git a/lms/djangoapps/verify_student/migrations/0001_initial.py b/lms/djangoapps/verify_student/migrations/0001_initial.py index 97337a34b4..469df3c7c6 100644 --- a/lms/djangoapps/verify_student/migrations/0001_initial.py +++ b/lms/djangoapps/verify_student/migrations/0001_initial.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - import django.db.models.deletion import django.utils.timezone import model_utils.fields @@ -75,7 +72,7 @@ class Migration(migrations.Migration): name='SoftwareSecurePhotoVerification', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('status', model_utils.fields.StatusField(default=u'created', max_length=100, verbose_name='status', no_check_for_status=True, choices=[(u'created', u'created'), (u'ready', u'ready'), (u'submitted', u'submitted'), (u'must_retry', u'must_retry'), (u'approved', u'approved'), (u'denied', u'denied')])), + ('status', model_utils.fields.StatusField(default='created', max_length=100, verbose_name='status', no_check_for_status=True, choices=[('created', 'created'), ('ready', 'ready'), ('submitted', 'submitted'), ('must_retry', 'must_retry'), ('approved', 'approved'), ('denied', 'denied')])), ('status_changed', model_utils.fields.MonitorField(default=django.utils.timezone.now, verbose_name='status changed', monitor='status')), ('name', models.CharField(max_length=255, blank=True)), ('face_image_url', models.URLField(max_length=255, blank=True)), @@ -124,7 +121,7 @@ class Migration(migrations.Migration): name='VerificationStatus', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('status', models.CharField(db_index=True, max_length=32, choices=[(u'submitted', u'submitted'), (u'approved', u'approved'), (u'denied', u'denied'), (u'error', u'error')])), + ('status', models.CharField(db_index=True, max_length=32, choices=[('submitted', 'submitted'), ('approved', 'approved'), ('denied', 'denied'), ('error', 'error')])), ('timestamp', models.DateTimeField(auto_now_add=True)), ('response', models.TextField(null=True, blank=True)), ('error', models.TextField(null=True, blank=True)), @@ -149,10 +146,10 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='verificationcheckpoint', - unique_together=set([('course_id', 'checkpoint_location')]), + unique_together={('course_id', 'checkpoint_location')}, ), migrations.AlterUniqueTogether( name='skippedreverification', - unique_together=set([('user', 'course_id')]), + unique_together={('user', 'course_id')}, ), ] diff --git a/lms/djangoapps/verify_student/migrations/0002_auto_20151124_1024.py b/lms/djangoapps/verify_student/migrations/0002_auto_20151124_1024.py index 4fc4b2ecea..5cac78e81d 100644 --- a/lms/djangoapps/verify_student/migrations/0002_auto_20151124_1024.py +++ b/lms/djangoapps/verify_student/migrations/0002_auto_20151124_1024.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models diff --git a/lms/djangoapps/verify_student/migrations/0003_auto_20151113_1443.py b/lms/djangoapps/verify_student/migrations/0003_auto_20151113_1443.py index 837de2c231..12c2c3f5a5 100644 --- a/lms/djangoapps/verify_student/migrations/0003_auto_20151113_1443.py +++ b/lms/djangoapps/verify_student/migrations/0003_auto_20151113_1443.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models diff --git a/lms/djangoapps/verify_student/migrations/0004_delete_historical_records.py b/lms/djangoapps/verify_student/migrations/0004_delete_historical_records.py index 816acc32c6..3005b2794d 100644 --- a/lms/djangoapps/verify_student/migrations/0004_delete_historical_records.py +++ b/lms/djangoapps/verify_student/migrations/0004_delete_historical_records.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models diff --git a/lms/djangoapps/verify_student/migrations/0005_remove_deprecated_models.py b/lms/djangoapps/verify_student/migrations/0005_remove_deprecated_models.py index 38f79bade3..e7a7f8e2c2 100644 --- a/lms/djangoapps/verify_student/migrations/0005_remove_deprecated_models.py +++ b/lms/djangoapps/verify_student/migrations/0005_remove_deprecated_models.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models @@ -21,7 +18,7 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='skippedreverification', - unique_together=set([]), + unique_together=set(), ), migrations.RemoveField( model_name='skippedreverification', @@ -33,7 +30,7 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='verificationcheckpoint', - unique_together=set([]), + unique_together=set(), ), migrations.RemoveField( model_name='verificationcheckpoint', diff --git a/lms/djangoapps/verify_student/migrations/0006_ssoverification.py b/lms/djangoapps/verify_student/migrations/0006_ssoverification.py index 4f53cb9079..c74b846b04 100644 --- a/lms/djangoapps/verify_student/migrations/0006_ssoverification.py +++ b/lms/djangoapps/verify_student/migrations/0006_ssoverification.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.12 on 2018-04-11 15:20 @@ -21,7 +20,7 @@ class Migration(migrations.Migration): name='SSOVerification', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('status', model_utils.fields.StatusField(choices=[(u'created', u'created'), (u'ready', u'ready'), (u'submitted', u'submitted'), (u'must_retry', u'must_retry'), (u'approved', u'approved'), (u'denied', u'denied')], default=u'created', max_length=100, no_check_for_status=True, verbose_name='status')), + ('status', model_utils.fields.StatusField(choices=[('created', 'created'), ('ready', 'ready'), ('submitted', 'submitted'), ('must_retry', 'must_retry'), ('approved', 'approved'), ('denied', 'denied')], default='created', max_length=100, no_check_for_status=True, verbose_name='status')), ('status_changed', model_utils.fields.MonitorField(default=django.utils.timezone.now, monitor='status', verbose_name='status changed')), ('name', models.CharField(blank=True, max_length=255)), ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), @@ -30,16 +29,16 @@ class Migration(migrations.Migration): 'identity_provider_type', models.CharField( choices=[ - (u'common.djangoapps.third_party_auth.models.OAuth2ProviderConfig', u'OAuth2 Provider'), - (u'common.djangoapps.third_party_auth.models.SAMLProviderConfig', u'SAML Provider'), - (u'common.djangoapps.third_party_auth.models.LTIProviderConfig', u'LTI Provider'), + ('common.djangoapps.third_party_auth.models.OAuth2ProviderConfig', 'OAuth2 Provider'), + ('common.djangoapps.third_party_auth.models.SAMLProviderConfig', 'SAML Provider'), + ('common.djangoapps.third_party_auth.models.LTIProviderConfig', 'LTI Provider'), ], - default=u'common.djangoapps.third_party_auth.models.SAMLProviderConfig', - help_text=u'Specifies which type of Identity Provider this verification originated from.', + default='common.djangoapps.third_party_auth.models.SAMLProviderConfig', + help_text='Specifies which type of Identity Provider this verification originated from.', max_length=100, ), ), - ('identity_provider_slug', models.SlugField(default=u'default', help_text=u'The slug uniquely identifying the Identity Provider this verification originated from.', max_length=30)), + ('identity_provider_slug', models.SlugField(default='default', help_text='The slug uniquely identifying the Identity Provider this verification originated from.', max_length=30)), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), diff --git a/lms/djangoapps/verify_student/migrations/0007_idverificationaggregate.py b/lms/djangoapps/verify_student/migrations/0007_idverificationaggregate.py index e7762f8a53..a1b41c14f8 100644 --- a/lms/djangoapps/verify_student/migrations/0007_idverificationaggregate.py +++ b/lms/djangoapps/verify_student/migrations/0007_idverificationaggregate.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.12 on 2018-04-12 15:22 @@ -22,7 +21,7 @@ class Migration(migrations.Migration): name='IDVerificationAggregate', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('status', model_utils.fields.StatusField(choices=[(u'created', u'created'), (u'ready', u'ready'), (u'submitted', u'submitted'), (u'must_retry', u'must_retry'), (u'approved', u'approved'), (u'denied', u'denied')], default=u'created', max_length=100, no_check_for_status=True, verbose_name='status')), + ('status', model_utils.fields.StatusField(choices=[('created', 'created'), ('ready', 'ready'), ('submitted', 'submitted'), ('must_retry', 'must_retry'), ('approved', 'approved'), ('denied', 'denied')], default='created', max_length=100, no_check_for_status=True, verbose_name='status')), ('status_changed', model_utils.fields.MonitorField(default=django.utils.timezone.now, monitor='status', verbose_name='status changed')), ('name', models.CharField(blank=True, max_length=255)), ('object_id', models.PositiveIntegerField()), diff --git a/lms/djangoapps/verify_student/migrations/0008_populate_idverificationaggregate.py b/lms/djangoapps/verify_student/migrations/0008_populate_idverificationaggregate.py index 528ab4655c..11ec5a77f1 100644 --- a/lms/djangoapps/verify_student/migrations/0008_populate_idverificationaggregate.py +++ b/lms/djangoapps/verify_student/migrations/0008_populate_idverificationaggregate.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.12 on 2018-04-11 19:15 diff --git a/lms/djangoapps/verify_student/migrations/0009_remove_id_verification_aggregate.py b/lms/djangoapps/verify_student/migrations/0009_remove_id_verification_aggregate.py index e2b8ea9cf1..4f89401b0f 100644 --- a/lms/djangoapps/verify_student/migrations/0009_remove_id_verification_aggregate.py +++ b/lms/djangoapps/verify_student/migrations/0009_remove_id_verification_aggregate.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.12 on 2018-04-27 16:27 diff --git a/lms/djangoapps/verify_student/migrations/0010_manualverification.py b/lms/djangoapps/verify_student/migrations/0010_manualverification.py index d50580181a..20d9d7d996 100644 --- a/lms/djangoapps/verify_student/migrations/0010_manualverification.py +++ b/lms/djangoapps/verify_student/migrations/0010_manualverification.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.13 on 2018-06-07 10:51 @@ -21,12 +20,12 @@ class Migration(migrations.Migration): name='ManualVerification', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('status', model_utils.fields.StatusField(choices=[(u'created', u'created'), (u'ready', u'ready'), (u'submitted', u'submitted'), (u'must_retry', u'must_retry'), (u'approved', u'approved'), (u'denied', u'denied')], default=u'created', max_length=100, no_check_for_status=True, verbose_name='status')), + ('status', model_utils.fields.StatusField(choices=[('created', 'created'), ('ready', 'ready'), ('submitted', 'submitted'), ('must_retry', 'must_retry'), ('approved', 'approved'), ('denied', 'denied')], default='created', max_length=100, no_check_for_status=True, verbose_name='status')), ('status_changed', model_utils.fields.MonitorField(default=django.utils.timezone.now, monitor='status', verbose_name='status changed')), ('name', models.CharField(blank=True, max_length=255)), ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True, db_index=True)), - ('reason', models.CharField(blank=True, help_text=u'Specifies the reason for manual verification of the user.', max_length=255)), + ('reason', models.CharField(blank=True, help_text='Specifies the reason for manual verification of the user.', max_length=255)), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), diff --git a/lms/djangoapps/verify_student/migrations/0011_add_fields_to_sspv.py b/lms/djangoapps/verify_student/migrations/0011_add_fields_to_sspv.py index 2ec2c1ce0e..8bbde2f84d 100644 --- a/lms/djangoapps/verify_student/migrations/0011_add_fields_to_sspv.py +++ b/lms/djangoapps/verify_student/migrations/0011_add_fields_to_sspv.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2019-01-10 09:19 diff --git a/lms/djangoapps/verify_student/migrations/0012_sspverificationretryconfig.py b/lms/djangoapps/verify_student/migrations/0012_sspverificationretryconfig.py index b69b5a3611..b4f042bdae 100644 --- a/lms/djangoapps/verify_student/migrations/0012_sspverificationretryconfig.py +++ b/lms/djangoapps/verify_student/migrations/0012_sspverificationretryconfig.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.26 on 2019-12-10 11:19 diff --git a/lms/djangoapps/verify_student/models.py b/lms/djangoapps/verify_student/models.py index 9c9d035b7e..c094d873b5 100644 --- a/lms/djangoapps/verify_student/models.py +++ b/lms/djangoapps/verify_student/models.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Models for Student Identity Verification @@ -20,7 +19,6 @@ from datetime import timedelta from email.utils import formatdate import requests -import six from config_models.models import ConfigurationModel from django.conf import settings from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user @@ -36,28 +34,24 @@ from model_utils.models import StatusModel, TimeStampedModel from opaque_keys.edx.django.models import CourseKeyField from lms.djangoapps.verify_student.ssencrypt import ( - encrypt_and_encode, decode_and_decrypt, + encrypt_and_encode, generate_signed_message, random_aes_key, - rsa_encrypt, - rsa_decrypt + rsa_decrypt, + rsa_encrypt ) from openedx.core.djangoapps.signals.signals import LEARNER_NOW_VERIFIED from openedx.core.storage import get_storage -from .utils import ( - auto_verify_for_testing_enabled, - earliest_allowed_verification_date, - submit_request_to_ss -) +from .utils import auto_verify_for_testing_enabled, earliest_allowed_verification_date, submit_request_to_ss log = logging.getLogger(__name__) def generateUUID(): # pylint: disable=invalid-name """ Utility function; generates UUIDs """ - return six.text_type(uuid.uuid4()) + return str(uuid.uuid4()) class VerificationException(Exception): @@ -87,7 +81,7 @@ def status_before_must_be(*valid_start_statuses): def with_status_check(obj, *args, **kwargs): if obj.status not in valid_start_statuses: exception_msg = ( - u"Error calling {} {}: status is '{}', must be one of: {}" + "Error calling {} {}: status is '{}', must be one of: {}" ).format(func, obj, obj.status, valid_start_statuses) raise VerificationException(exception_msg) return func(obj, *args, **kwargs) @@ -107,7 +101,7 @@ class IDVerificationAttempt(StatusModel): .. pii_types: name .. pii_retirement: retained """ - STATUS = Choices(u'created', u'ready', u'submitted', u'must_retry', u'approved', u'denied') + STATUS = Choices('created', 'ready', 'submitted', 'must_retry', 'approved', 'denied') user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE) # They can change their name later on, so we want to copy the value here so @@ -130,7 +124,7 @@ class IDVerificationAttempt(StatusModel): default=expiration_default ) - class Meta(object): + class Meta: app_label = "verify_student" abstract = True ordering = ['-created_at'] @@ -179,11 +173,11 @@ class ManualVerification(IDVerificationAttempt): max_length=255, blank=True, help_text=( - u'Specifies the reason for manual verification of the user.' + 'Specifies the reason for manual verification of the user.' ) ) - class Meta(object): + class Meta: app_label = 'verify_student' def __str__(self): @@ -209,13 +203,13 @@ class SSOVerification(IDVerificationAttempt): .. no_pii: """ - OAUTH2 = u'common.djangoapps.third_party_auth.models.OAuth2ProviderConfig' - SAML = u'common.djangoapps.third_party_auth.models.SAMLProviderConfig' - LTI = u'common.djangoapps.third_party_auth.models.LTIProviderConfig' + OAUTH2 = 'common.djangoapps.third_party_auth.models.OAuth2ProviderConfig' + SAML = 'common.djangoapps.third_party_auth.models.SAMLProviderConfig' + LTI = 'common.djangoapps.third_party_auth.models.LTIProviderConfig' IDENTITY_PROVIDER_TYPE_CHOICES = ( - (OAUTH2, u'OAuth2 Provider'), - (SAML, u'SAML Provider'), - (LTI, u'LTI Provider'), + (OAUTH2, 'OAuth2 Provider'), + (SAML, 'SAML Provider'), + (LTI, 'LTI Provider'), ) identity_provider_type = models.CharField( @@ -224,17 +218,17 @@ class SSOVerification(IDVerificationAttempt): choices=IDENTITY_PROVIDER_TYPE_CHOICES, default=SAML, help_text=( - u'Specifies which type of Identity Provider this verification originated from.' + 'Specifies which type of Identity Provider this verification originated from.' ) ) identity_provider_slug = models.SlugField( - max_length=30, db_index=True, default=u'default', + max_length=30, db_index=True, default='default', help_text=( - u'The slug uniquely identifying the Identity Provider this verification originated from.' + 'The slug uniquely identifying the Identity Provider this verification originated from.' )) - class Meta(object): + class Meta: app_label = "verify_student" def __str__(self): @@ -251,7 +245,7 @@ class SSOVerification(IDVerificationAttempt): """ Send a signal indicating that this verification was approved. """ - log.info(u"Verification for user '{user_id}' approved by '{reviewer}' SSO.".format( + log.info("Verification for user '{user_id}' approved by '{reviewer}' SSO.".format( user_id=self.user, reviewer=approved_by )) @@ -261,7 +255,7 @@ class SSOVerification(IDVerificationAttempt): user=self.user ) - message = u'LEARNER_NOW_VERIFIED signal fired for {user} from SSOVerification' + message = 'LEARNER_NOW_VERIFIED signal fired for {user} from SSOVerification' log.info(message.format(user=self.user.username)) @@ -357,7 +351,7 @@ class PhotoVerification(IDVerificationAttempt): # capturing it so that we can later query for the common problems. error_code = models.CharField(blank=True, max_length=50) - class Meta(object): + class Meta: app_label = "verify_student" abstract = True ordering = ['-created_at'] @@ -446,7 +440,7 @@ class PhotoVerification(IDVerificationAttempt): if self.status == self.STATUS.approved: return - log.info(u"Verification for user '{user_id}' approved by '{reviewer}'.".format( + log.info("Verification for user '{user_id}' approved by '{reviewer}'.".format( user_id=self.user, reviewer=user_id )) self.error_msg = "" # reset, in case this attempt was denied before @@ -464,7 +458,7 @@ class PhotoVerification(IDVerificationAttempt): user=self.user ) - message = u'LEARNER_NOW_VERIFIED signal fired for {user} from PhotoVerification' + message = 'LEARNER_NOW_VERIFIED signal fired for {user} from PhotoVerification' log.info(message.format(user=self.user.username)) @status_before_must_be("ready", "must_retry") @@ -548,7 +542,7 @@ class PhotoVerification(IDVerificationAttempt): lets you amend the error message in case there were additional details to be made. """ - log.info(u"Verification for user '{user_id}' denied by '{reviewer}'.".format( + log.info("Verification for user '{user_id}' denied by '{reviewer}'.".format( user_id=self.user, reviewer=reviewing_user )) self.error_msg = error_msg @@ -658,7 +652,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification): """Use expiry_date for older entries if it still exists.""" if self.expiry_date: return self.expiry_date - return super(SoftwareSecurePhotoVerification, self).expiration_datetime # lint-amnesty, pylint: disable=super-with-arguments + return super().expiration_datetime @classmethod def get_initial_verification(cls, user, earliest_allowed_date=None): @@ -725,10 +719,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification): aes_key_str = settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["FACE_IMAGE_AES_KEY"] - if six.PY3: - aes_key = codecs.decode(aes_key_str, "hex") - else: - aes_key = aes_key_str.decode("hex") + aes_key = codecs.decode(aes_key_str, "hex") encrypted_data = encrypt_and_encode(img_data, aes_key) @@ -767,10 +758,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification): self._save_image_to_storage(path, encrypted_data) # Update our record fields - if six.PY3: - self.photo_id_key = codecs.encode(rsa_encrypted_aes_key, 'base64').decode('utf-8') - else: - self.photo_id_key = rsa_encrypted_aes_key.encode('base64') + self.photo_id_key = codecs.encode(rsa_encrypted_aes_key, 'base64').decode('utf-8') self.save() @@ -796,15 +784,12 @@ class SoftwareSecurePhotoVerification(PhotoVerification): aes_key_str = settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["FACE_IMAGE_AES_KEY"] try: - if six.PY3: - aes_key = codecs.decode(aes_key_str, "hex") - else: - aes_key = aes_key_str.decode("hex") + aes_key = codecs.decode(aes_key_str, "hex") img_bytes = decode_and_decrypt(byte_img_data, aes_key) return img_bytes except Exception as e: # pylint: disable=broad-except - log.exception(u'Failed to decrypt face image due to an exception: %s', e) + log.exception('Failed to decrypt face image due to an exception: %s', e) return None @status_before_must_be("must_retry", "submitted", "approved", "denied") @@ -828,7 +813,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification): img_bytes = decode_and_decrypt(byte_img_data, decrypted_aes_key) return img_bytes except Exception as e: # pylint: disable=broad-except - log.exception(u'Failed to decrypt photo id image due to an exception: %s', e) + log.exception('Failed to decrypt photo id image due to an exception: %s', e) return None @status_before_must_be("must_retry", "ready", "submitted") @@ -894,7 +879,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification): message_groups = json.loads(self.error_msg) for message_group in message_groups: - messages = messages.union(set(*six.itervalues(message_group))) + messages = messages.union(set(*message_group.values())) for message in messages: parsed_error = error_map.get(message) @@ -902,9 +887,9 @@ class SoftwareSecurePhotoVerification(PhotoVerification): if parsed_error: parsed_errors.append(parsed_error) else: - log.debug(u'Ignoring photo verification error message: %s', message) + log.debug('Ignoring photo verification error message: %s', message) except Exception: # pylint: disable=broad-except - log.exception(u'Failed to parse error message for SoftwareSecurePhotoVerification %d', self.pk) + log.exception('Failed to parse error message for SoftwareSecurePhotoVerification %d', self.pk) return parsed_errors @@ -1042,7 +1027,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification): headers, body = self.create_request() header_txt = "\n".join( - u"{}: {}".format(h, v) for h, v in sorted(headers.items()) + f"{h}: {v}" for h, v in sorted(headers.items()) ) body_txt = json.dumps(body, indent=2, sort_keys=True, ensure_ascii=False) @@ -1106,19 +1091,19 @@ class VerificationDeadline(TimeStampedModel): .. no_pii: """ - class Meta(object): + class Meta: app_label = "verify_student" course_key = CourseKeyField( max_length=255, db_index=True, unique=True, - help_text=ugettext_lazy(u"The course for which this deadline applies"), + help_text=ugettext_lazy("The course for which this deadline applies"), ) deadline = models.DateTimeField( help_text=ugettext_lazy( - u"The datetime after which users are no longer allowed " + "The datetime after which users are no longer allowed " "to submit photos for verification." ) ) @@ -1204,7 +1189,7 @@ class SSPVerificationRetryConfig(ConfigurationModel): # pylint: disable=model-m to retry_failed_photo_verifications management command """ - class Meta(object): + class Meta: app_label = 'verify_student' verbose_name = 'sspv retry student argument' @@ -1215,4 +1200,4 @@ class SSPVerificationRetryConfig(ConfigurationModel): # pylint: disable=model-m ) def __str__(self): - return six.text_type(self.arguments) + return str(self.arguments) diff --git a/lms/djangoapps/verify_student/services.py b/lms/djangoapps/verify_student/services.py index a34a53d39e..31438cc93d 100644 --- a/lms/djangoapps/verify_student/services.py +++ b/lms/djangoapps/verify_student/services.py @@ -13,9 +13,9 @@ from django.utils.timezone import now from django.utils.translation import ugettext as _ from common.djangoapps.course_modes.models import CourseMode +from common.djangoapps.student.models import User from lms.djangoapps.verify_student.utils import is_verification_expiring_soon from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers -from common.djangoapps.student.models import User from .models import ManualVerification, SoftwareSecurePhotoVerification, SSOVerification from .utils import most_recent_verification @@ -23,7 +23,7 @@ from .utils import most_recent_verification log = logging.getLogger(__name__) -class XBlockVerificationService(object): +class XBlockVerificationService: """ Learner verification XBlock service. """ @@ -52,7 +52,7 @@ class XBlockVerificationService(object): return IDVerificationService.get_verify_location() -class IDVerificationService(object): +class IDVerificationService: """ Learner verification service interface for callers within edx-platform. """ @@ -212,7 +212,7 @@ class IDVerificationService(object): if attempt.expiration_datetime < now() and attempt.status == 'approved': if user_status['should_display']: user_status['status'] = 'expired' - user_status['error'] = _(u"Your {platform_name} verification has expired.").format( + user_status['error'] = _("Your {platform_name} verification has expired.").format( platform_name=configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME), ) else: @@ -262,7 +262,7 @@ class IDVerificationService(object): Returns a string: Returns URL for IDV on Account Microfrontend """ - location = '{}/id-verification'.format(settings.ACCOUNT_MICROFRONTEND_URL) + location = f'{settings.ACCOUNT_MICROFRONTEND_URL}/id-verification' if course_id: location += '?course_id={}'.format(quote(str(course_id))) return location diff --git a/lms/djangoapps/verify_student/ssencrypt.py b/lms/djangoapps/verify_student/ssencrypt.py index 9481341700..47bc243e9c 100644 --- a/lms/djangoapps/verify_student/ssencrypt.py +++ b/lms/djangoapps/verify_student/ssencrypt.py @@ -33,7 +33,6 @@ from cryptography.hazmat.primitives.ciphers.algorithms import AES from cryptography.hazmat.primitives.ciphers.modes import CBC from cryptography.hazmat.primitives.hashes import SHA1 from cryptography.hazmat.primitives.padding import PKCS7 -from six import text_type log = logging.getLogger(__name__) @@ -84,10 +83,7 @@ def generate_aes_iv(key): Return the initialization vector Software Secure expects for a given AES key (they hash it a couple of times and take a substring). """ - if six.PY3: - return md5(key + md5(key).hexdigest().encode('utf-8')).hexdigest()[:AES_BLOCK_SIZE_BYTES].encode('utf-8') - else: - return md5(key + md5(key).hexdigest()).hexdigest()[:AES_BLOCK_SIZE_BYTES] + return md5(key + md5(key).hexdigest().encode('utf-8')).hexdigest()[:AES_BLOCK_SIZE_BYTES].encode('utf-8') def random_aes_key(): @@ -114,9 +110,9 @@ def rsa_encrypt(data, rsa_pub_key_bytes): """ `rsa_pub_key_bytes` is a byte sequence with the public key """ - if isinstance(data, text_type): + if isinstance(data, str): data = data.encode('utf-8') - if isinstance(rsa_pub_key_bytes, text_type): + if isinstance(rsa_pub_key_bytes, str): rsa_pub_key_bytes = rsa_pub_key_bytes.encode('utf-8') if rsa_pub_key_bytes.startswith(b'-----'): key = serialization.load_pem_public_key(rsa_pub_key_bytes, backend=default_backend()) @@ -131,9 +127,9 @@ def rsa_decrypt(data, rsa_priv_key_bytes): """ When given some `data` and an RSA private key, decrypt the data """ - if isinstance(data, text_type): + if isinstance(data, str): data = data.encode('utf-8') - if isinstance(rsa_priv_key_bytes, text_type): + if isinstance(rsa_priv_key_bytes, str): rsa_priv_key_bytes = rsa_priv_key_bytes.encode('utf-8') if rsa_priv_key_bytes.startswith(b'-----'): key = serialization.load_pem_private_key(rsa_priv_key_bytes, password=None, backend=default_backend()) @@ -157,12 +153,12 @@ def has_valid_signature(method, headers_dict, body_dict, access_key, secret_key) if post_access_key != access_key: log.error("Posted access key does not match ours") - log.debug(u"Their access: %s; Our access: %s", post_access_key, access_key) + log.debug("Their access: %s; Our access: %s", post_access_key, access_key) return False if post_signature != expected_signature: log.error("Posted signature does not match expected") - log.debug(u"Their sig: %s; Expected: %s", post_signature, expected_signature) + log.debug("Their sig: %s; Expected: %s", post_signature, expected_signature) return False return True @@ -177,7 +173,7 @@ def generate_signed_message(method, headers_dict, body_dict, access_key, secret_ # hmac needs a byte string for it's starting key, can't be unicode. hashed = hmac.new(secret_key.encode('utf-8'), message.encode('utf-8'), sha256) signature = binascii.b2a_base64(hashed.digest()).rstrip(b'\n').decode('utf-8') - authorization_header = u"SSI {}:{}".format(access_key, signature) + authorization_header = f"SSI {access_key}:{signature}" message += '\n' return message, signature, authorization_header @@ -221,14 +217,14 @@ def body_string(body_dict, prefix=""): if isinstance(value, (list, tuple)): for i, arr in enumerate(value): if isinstance(arr, dict): - body_list.append(body_string(arr, u"{}.{}.".format(key, i))) + body_list.append(body_string(arr, f"{key}.{i}.")) else: - body_list.append(u"{}.{}:{}\n".format(key, i, arr)) + body_list.append(f"{key}.{i}:{arr}\n") elif isinstance(value, dict): body_list.append(body_string(value, key + ":")) else: if value is None: value = "null" - body_list.append(u"{}{}:{}\n".format(prefix, key, value)) + body_list.append(f"{prefix}{key}:{value}\n") return "".join(body_list) # Note that trailing \n's are important diff --git a/lms/djangoapps/verify_student/tasks.py b/lms/djangoapps/verify_student/tasks.py index e2987a2548..182f8027a5 100644 --- a/lms/djangoapps/verify_student/tasks.py +++ b/lms/djangoapps/verify_student/tasks.py @@ -91,7 +91,7 @@ def send_verification_status_email(context): msg.content_subtype = 'html' msg.send(fail_silently=False) except SMTPException: - log.warning(u"Failure in sending verification status e-mail to %s", dest_addr) + log.warning("Failure in sending verification status e-mail to %s", dest_addr) @shared_task( diff --git a/lms/djangoapps/verify_student/tests/__init__.py b/lms/djangoapps/verify_student/tests/__init__.py index ef1b27bc9f..cea71e52c3 100644 --- a/lms/djangoapps/verify_student/tests/__init__.py +++ b/lms/djangoapps/verify_student/tests/__init__.py @@ -8,8 +8,8 @@ from django.db import DEFAULT_DB_ALIAS from django.test import TestCase from django.utils.timezone import now -from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification from common.djangoapps.student.tests.factories import UserFactory +from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification class TestVerificationBase(TestCase): @@ -85,7 +85,7 @@ class TestVerificationBase(TestCase): if not user: user = UserFactory.create() attempt = SoftwareSecurePhotoVerification(user=user) - user.profile.name = u"Rust\u01B4" + user.profile.name = "Rust\u01B4" attempt.upload_face_image("Just pretend this is image data") attempt.upload_photo_id_image("Hey, we're a photo ID") diff --git a/lms/djangoapps/verify_student/tests/factories.py b/lms/djangoapps/verify_student/tests/factories.py index 94bf798d5a..2c5fe3ac83 100644 --- a/lms/djangoapps/verify_student/tests/factories.py +++ b/lms/djangoapps/verify_student/tests/factories.py @@ -9,14 +9,14 @@ from django.conf import settings # lint-amnesty, pylint: disable=unused-import from django.utils.timezone import now # lint-amnesty, pylint: disable=unused-import from factory.django import DjangoModelFactory -from lms.djangoapps.verify_student.models import SSOVerification, SoftwareSecurePhotoVerification +from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, SSOVerification class SoftwareSecurePhotoVerificationFactory(DjangoModelFactory): """ Factory for SoftwareSecurePhotoVerification """ - class Meta(object): + class Meta: model = SoftwareSecurePhotoVerification status = 'approved' diff --git a/lms/djangoapps/verify_student/tests/fake_software_secure.py b/lms/djangoapps/verify_student/tests/fake_software_secure.py index 9303b7e9c9..b4d1a00428 100644 --- a/lms/djangoapps/verify_student/tests/fake_software_secure.py +++ b/lms/djangoapps/verify_student/tests/fake_software_secure.py @@ -36,7 +36,7 @@ class SoftwareSecureFakeView(View): access_key = settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["API_ACCESS_KEY"] context = { 'receipt_id': None, - 'authorization_code': u'SIS {}:0000'.format(access_key), + 'authorization_code': f'SIS {access_key}:0000', 'results_callback': reverse('verify_student_results_callback') } diff --git a/lms/djangoapps/verify_student/tests/test_fake_software_secure.py b/lms/djangoapps/verify_student/tests/test_fake_software_secure.py index a4e0f7f7bc..ce5efbdace 100644 --- a/lms/djangoapps/verify_student/tests/test_fake_software_secure.py +++ b/lms/djangoapps/verify_student/tests/test_fake_software_secure.py @@ -3,12 +3,13 @@ Tests for the fake software secure response. """ -from django.test import TestCase -from mock import patch +from unittest.mock import patch + +from django.test import TestCase -from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification from common.djangoapps.student.tests.factories import UserFactory from common.djangoapps.util.testing import UrlResetMixin +from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification class SoftwareSecureFakeViewTest(UrlResetMixin, TestCase): @@ -21,7 +22,7 @@ class SoftwareSecureFakeViewTest(UrlResetMixin, TestCase): def setUp(self, **kwargs): enable_software_secure_fake = kwargs.get('enable_software_secure_fake', False) with patch.dict('django.conf.settings.FEATURES', {'ENABLE_SOFTWARE_SECURE_FAKE': enable_software_secure_fake}): - super(SoftwareSecureFakeViewTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create(username="test", password="test") self.attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user) @@ -35,7 +36,7 @@ class SoftwareSecureFakeViewDisabledTest(SoftwareSecureFakeViewTest): """ def setUp(self): # lint-amnesty, pylint: disable=arguments-differ - super(SoftwareSecureFakeViewDisabledTest, self).setUp(enable_software_secure_fake=False) # lint-amnesty, pylint: disable=super-with-arguments + super().setUp(enable_software_secure_fake=False) def test_get_method_without_enable_feature_flag(self): """ @@ -56,7 +57,7 @@ class SoftwareSecureFakeViewEnabledTest(SoftwareSecureFakeViewTest): """ def setUp(self): # lint-amnesty, pylint: disable=arguments-differ - super(SoftwareSecureFakeViewEnabledTest, self).setUp(enable_software_secure_fake=True) # lint-amnesty, pylint: disable=super-with-arguments + super().setUp(enable_software_secure_fake=True) def test_get_method_without_logged_in_user(self): """ diff --git a/lms/djangoapps/verify_student/tests/test_integration.py b/lms/djangoapps/verify_student/tests/test_integration.py index 4595c22d44..5967e41985 100644 --- a/lms/djangoapps/verify_student/tests/test_integration.py +++ b/lms/djangoapps/verify_student/tests/test_integration.py @@ -2,7 +2,6 @@ Integration tests of the payment flow, including course mode selection. """ -import six from django.urls import reverse from common.djangoapps.course_modes.tests.factories import CourseModeFactory @@ -23,7 +22,7 @@ class TestProfEdVerification(ModuleStoreTestCase): MIN_PRICE = 1438 def setUp(self): - super(TestProfEdVerification, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create(username="rusty", password="test") self.client.login(username="rusty", password="test") @@ -38,7 +37,7 @@ class TestProfEdVerification(ModuleStoreTestCase): self.urls = { 'course_modes_choose': reverse( 'course_modes_choose', - args=[six.text_type(self.course_key)] + args=[str(self.course_key)] ), 'verify_student_start_flow': IDVerificationService.get_verify_location(self.course_key), diff --git a/lms/djangoapps/verify_student/tests/test_models.py b/lms/djangoapps/verify_student/tests/test_models.py index 09cdc72866..340a9680ac 100644 --- a/lms/djangoapps/verify_student/tests/test_models.py +++ b/lms/djangoapps/verify_student/tests/test_models.py @@ -2,17 +2,18 @@ import base64 from datetime import datetime, timedelta +from unittest import mock +from unittest.mock import patch + import pytest import ddt -import mock import requests.exceptions import simplejson as json from django.conf import settings from django.utils.timezone import now from freezegun import freeze_time -from mock import patch -from six.moves import range +from common.djangoapps.student.tests.factories import UserFactory from common.test.utils import MockS3BotoMixin from lms.djangoapps.verify_student.models import ( ManualVerification, @@ -21,7 +22,6 @@ from lms.djangoapps.verify_student.models import ( SSOVerification, VerificationException ) -from common.djangoapps.student.tests.factories import UserFactory from lms.djangoapps.verify_student.tests import TestVerificationBase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase @@ -175,15 +175,15 @@ class TestPhotoVerification(TestVerificationBase, MockS3BotoMixin, ModuleStoreTe was when you submitted it. """ user = UserFactory.create() - user.profile.name = u"Jack \u01B4" # gratuious non-ASCII char to test encodings + user.profile.name = "Jack \u01B4" # gratuious non-ASCII char to test encodings attempt = SoftwareSecurePhotoVerification(user=user) - user.profile.name = u"Clyde \u01B4" + user.profile.name = "Clyde \u01B4" attempt.mark_ready() - user.profile.name = u"Rusty \u01B4" + user.profile.name = "Rusty \u01B4" - assert u'Clyde ƴ' == attempt.name + assert 'Clyde ƴ' == attempt.name def test_submissions(self): """Test that we set our status correctly after a submission.""" @@ -222,7 +222,7 @@ class TestPhotoVerification(TestVerificationBase, MockS3BotoMixin, ModuleStoreTe @ddt.data( 'Not Provided', '{"IdReasons": ["Not provided"]}', - u'[{"ïḋṚëäṡöṅṡ": ["Ⓝⓞⓣ ⓟⓡⓞⓥⓘⓓⓔⓓ "]}]', + '[{"ïḋṚëäṡöṅṡ": ["Ⓝⓞⓣ ⓟⓡⓞⓥⓘⓓⓔⓓ "]}]', ) def test_parse_error_msg_failure(self, msg): user = UserFactory.create() @@ -286,7 +286,7 @@ class TestPhotoVerification(TestVerificationBase, MockS3BotoMixin, ModuleStoreTe Retire user with record(s) in table """ user = UserFactory.create() - user.profile.name = u"Enrique" + user.profile.name = "Enrique" attempt = SoftwareSecurePhotoVerification(user=user) # Populate Record diff --git a/lms/djangoapps/verify_student/tests/test_services.py b/lms/djangoapps/verify_student/tests/test_services.py index fb1058aa04..ab36d3b8bf 100644 --- a/lms/djangoapps/verify_student/tests/test_services.py +++ b/lms/djangoapps/verify_student/tests/test_services.py @@ -1,9 +1,9 @@ -# -*- coding: utf-8 -*- """ Tests for the service classes in verify_student. """ from datetime import datetime, timedelta +from unittest.mock import patch import ddt from django.conf import settings @@ -11,7 +11,6 @@ from django.test import TestCase from django.utils.timezone import now from django.utils.translation import ugettext as _ from freezegun import freeze_time -from mock import patch from pytz import utc from common.djangoapps.student.tests.factories import UserFactory @@ -161,7 +160,7 @@ class TestIDVerificationService(ModuleStoreTestCase): Test for the path to the IDV flow with no course key given """ path = IDVerificationService.get_verify_location() - expected_path = '{}/id-verification'.format(settings.ACCOUNT_MICROFRONTEND_URL) + expected_path = f'{settings.ACCOUNT_MICROFRONTEND_URL}/id-verification' assert path == expected_path def test_get_verify_location_from_course_id(self): @@ -170,7 +169,7 @@ class TestIDVerificationService(ModuleStoreTestCase): """ course = CourseFactory.create(org='Robot', number='999', display_name='Test Course') path = IDVerificationService.get_verify_location(course.id) - expected_path = '{}/id-verification'.format(settings.ACCOUNT_MICROFRONTEND_URL) + expected_path = f'{settings.ACCOUNT_MICROFRONTEND_URL}/id-verification' assert path == (expected_path + '?course_id=Robot/999/Test_Course') def test_get_verify_location_from_string(self): @@ -178,7 +177,7 @@ class TestIDVerificationService(ModuleStoreTestCase): Test for the path to the IDV flow with a course key string """ path = IDVerificationService.get_verify_location('course-v1:edX+DemoX+Demo_Course') - expected_path = '{}/id-verification'.format(settings.ACCOUNT_MICROFRONTEND_URL) + expected_path = f'{settings.ACCOUNT_MICROFRONTEND_URL}/id-verification' assert path == (expected_path + '?course_id=course-v1%3AedX%2BDemoX%2BDemo_Course') @@ -192,7 +191,7 @@ class TestIDVerificationServiceUserStatus(TestCase): we just put everything inside of a frozen time """ def setUp(self): - super(TestIDVerificationServiceUserStatus, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create() def test_no_verification(self): @@ -290,7 +289,7 @@ class TestIDVerificationServiceUserStatus(TestCase): frozen_datetime.move_to('2016-07-11') expected_status = { 'status': 'expired', - 'error': _(u"Your {platform_name} verification has expired.").format( + 'error': _("Your {platform_name} verification has expired.").format( platform_name=configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME) ), 'should_display': True, diff --git a/lms/djangoapps/verify_student/tests/test_signals.py b/lms/djangoapps/verify_student/tests/test_signals.py index aa634ec81e..1c9888fbcf 100644 --- a/lms/djangoapps/verify_student/tests/test_signals.py +++ b/lms/djangoapps/verify_student/tests/test_signals.py @@ -7,11 +7,11 @@ from datetime import timedelta from django.utils.timezone import now +from common.djangoapps.student.tests.factories import UserFactory from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline from lms.djangoapps.verify_student.signals import _listen_for_course_publish, _listen_for_lms_retire from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory from openedx.core.djangoapps.user_api.accounts.tests.retirement_helpers import fake_completed_retirement -from common.djangoapps.student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -22,7 +22,7 @@ class VerificationDeadlineSignalTest(ModuleStoreTestCase): """ def setUp(self): - super(VerificationDeadlineSignalTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.end = now().replace(microsecond=0) + timedelta(days=7) self.course = CourseFactory.create(end=self.end) VerificationDeadline.objects.all().delete() diff --git a/lms/djangoapps/verify_student/tests/test_tasks.py b/lms/djangoapps/verify_student/tests/test_tasks.py index c3ea1777cd..b236dbb356 100644 --- a/lms/djangoapps/verify_student/tests/test_tasks.py +++ b/lms/djangoapps/verify_student/tests/test_tasks.py @@ -1,9 +1,10 @@ # lint-amnesty, pylint: disable=missing-module-docstring # Lots of patching to stub in our own settings, and HTTP posting +from unittest import mock +from unittest.mock import patch + import ddt -import mock from django.conf import settings -from mock import patch from common.test.utils import MockS3BotoMixin from lms.djangoapps.verify_student.tests import TestVerificationBase diff --git a/lms/djangoapps/verify_student/tests/test_utils.py b/lms/djangoapps/verify_student/tests/test_utils.py index 91e553ba59..b3cb348e99 100644 --- a/lms/djangoapps/verify_student/tests/test_utils.py +++ b/lms/djangoapps/verify_student/tests/test_utils.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Tests for verify_student utility functions. """ @@ -6,21 +5,21 @@ Tests for verify_student utility functions. import unittest from datetime import timedelta +from unittest import mock +from unittest.mock import patch import ddt -import mock from django.conf import settings from django.utils import timezone -from mock import patch from pytest import mark +from common.djangoapps.student.tests.factories import UserFactory from lms.djangoapps.verify_student.models import ManualVerification, SoftwareSecurePhotoVerification, SSOVerification from lms.djangoapps.verify_student.utils import ( most_recent_verification, submit_request_to_ss, verification_for_datetime ) -from common.djangoapps.student.tests.factories import UserFactory FAKE_SETTINGS = { "DAYS_GOOD_FOR": 10, diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index 88b4ab78f2..2801a0a5d1 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -1,18 +1,18 @@ -# encoding: utf-8 """ Tests of verify_student views. """ -from datetime import timedelta -from uuid import uuid4 - import base64 import codecs +import urllib +from datetime import timedelta +from unittest import mock +from unittest.mock import Mock, patch +from uuid import uuid4 + import ddt import httpretty -import mock import simplejson as json -import six from bs4 import BeautifulSoup from django.conf import settings from django.core import mail @@ -21,29 +21,27 @@ from django.test.client import Client, RequestFactory from django.test.utils import override_settings from django.urls import reverse from django.utils.timezone import now -from mock import Mock, patch from opaque_keys.edx.locator import CourseLocator -from six.moves import zip from waffle.testutils import override_switch -from common.test.utils import MockS3BotoMixin, XssTestMixin from common.djangoapps.course_modes.models import CourseMode from common.djangoapps.course_modes.tests.factories import CourseModeFactory +from common.djangoapps.student.models import CourseEnrollment +from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory +from common.djangoapps.util.testing import UrlResetMixin +from common.test.utils import MockS3BotoMixin, XssTestMixin from lms.djangoapps.commerce.models import CommerceConfiguration from lms.djangoapps.commerce.tests import TEST_API_URL, TEST_PAYMENT_DATA, TEST_PUBLIC_URL_ROOT from lms.djangoapps.commerce.tests.mocks import mock_payment_processors from lms.djangoapps.commerce.utils import EcommerceService from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline from lms.djangoapps.verify_student.services import IDVerificationService -from lms.djangoapps.verify_student.views import PayAndVerifyView, checkout_with_ecommerce_service, render_to_response from lms.djangoapps.verify_student.ssencrypt import encrypt_and_encode, rsa_encrypt +from lms.djangoapps.verify_student.tests import TestVerificationBase +from lms.djangoapps.verify_student.views import PayAndVerifyView, checkout_with_ecommerce_service, render_to_response from openedx.core.djangoapps.embargo.test_utils import restrict_course from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme from openedx.core.djangoapps.user_api.accounts.api import get_account_settings -from common.djangoapps.student.models import CourseEnrollment -from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory -from common.djangoapps.util.testing import UrlResetMixin -from lms.djangoapps.verify_student.tests import TestVerificationBase from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase @@ -104,7 +102,7 @@ def _mock_payment_processors(): """ httpretty.register_uri( httpretty.GET, - "{}/payment/processors/".format(TEST_API_URL), + f"{TEST_API_URL}/payment/processors/", body=json.dumps(['foo', 'bar']), content_type="application/json", ) @@ -117,7 +115,7 @@ class StartView(TestCase): """ def start_url(self, course_id=""): - return "/verify_student/{0}".format(six.moves.urllib.parse.quote(course_id)) + return "/verify_student/{}".format(urllib.parse.quote(course_id)) def test_start_new_verification(self): """ @@ -151,7 +149,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin, Tes @mock.patch.dict(settings.FEATURES, {'EMBARGO': True}) def setUp(self): - super(TestPayAndVerifyView, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) result = self.client.login(username=self.USERNAME, password=self.PASSWORD) assert result, 'Could not log in' @@ -193,7 +191,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin, Tes configuration = CommerceConfiguration.objects.create(checkout_on_ecommerce_service=True) checkout_page = configuration.basket_checkout_page checkout_page += "?utm_source=test" - httpretty.register_uri(httpretty.GET, "{}{}".format(TEST_PUBLIC_URL_ROOT, checkout_page)) + httpretty.register_uri(httpretty.GET, f"{TEST_PUBLIC_URL_ROOT}{checkout_page}") course = self._create_course('verified', sku=sku) self._enroll(course.id) @@ -202,7 +200,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin, Tes url_with_utm = 'http://www.example.com/basket/add/?utm_source=test&sku=TESTSKU' with mock.patch.object(EcommerceService, 'get_checkout_page_url', return_value=url_with_utm): response = self._get_page('verify_student_start_flow', course.id, expected_status_code=302) - expected_page = '{}{}&sku={}'.format(TEST_PUBLIC_URL_ROOT, checkout_page, sku) + expected_page = f'{TEST_PUBLIC_URL_ROOT}{checkout_page}&sku={sku}' self.assertRedirects(response, expected_page, fetch_redirect_response=False) @ddt.data( @@ -588,8 +586,8 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin, Tes course = self._create_course("verified") response = self._get_page(url_name, course.id, expected_status_code=302) - original_url = reverse(url_name, kwargs={'course_id': six.text_type(course.id)}) - login_url = u"{login_url}?next={original_url}".format( + original_url = reverse(url_name, kwargs={'course_id': str(course.id)}) + login_url = "{login_url}?next={original_url}".format( login_url=reverse('signin_user'), original_url=original_url ) @@ -688,7 +686,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin, Tes # Expect that the expiration date is set response = self._get_page(payment_flow, course.id) data = self._get_page_data(response) - assert data['verification_deadline'] == six.text_type(deadline) + assert data['verification_deadline'] == str(deadline) def test_course_mode_expired(self): deadline = now() + timedelta(days=-360) @@ -756,7 +754,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin, Tes # Check that the verification deadline (rather than the upgrade deadline) is displayed if verification_deadline is not None: - assert data['verification_deadline'] == six.text_type(verification_deadline) + assert data['verification_deadline'] == str(verification_deadline) else: assert data['verification_deadline'] == '' @@ -887,7 +885,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin, Tes """Set the contribution amount pre-filled in a session var. """ session = self.client.session session["donation_for_course"] = { - six.text_type(course_id): amount + str(course_id): amount } session.save() @@ -895,7 +893,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin, Tes @override_settings(ECOMMERCE_API_URL=TEST_API_URL) def _get_page(self, url_name, course_key, expected_status_code=200, skip_first_step=False, assert_headers=False): """Retrieve one of the verification pages. """ - url = reverse(url_name, kwargs={"course_id": six.text_type(course_key)}) + url = reverse(url_name, kwargs={"course_id": str(course_key)}) if skip_first_step: url += "?skip-first-step=1" @@ -930,11 +928,11 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin, Tes def _assert_requirements_displayed(self, response, requirements): """Check that requirements are displayed on the page. """ response_dict = self._get_page_data(response) - for req, displayed in six.iteritems(response_dict['requirements']): + for req, displayed in response_dict['requirements'].items(): if req in requirements: - assert displayed, "Expected '{req}' requirement to be displayed".format(req=req) + assert displayed, f"Expected '{req}' requirement to be displayed" else: - assert not displayed, "Expected '{req}' requirement to be displayed".format(req=req) + assert not displayed, f"Expected '{req}' requirement to be hidden" def _assert_course_details(self, response, course_key, display_name, url): """Check the course information on the page. """ @@ -986,7 +984,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin, Tes def _assert_redirects_to_start_flow(self, response, course_id): """Check that the page redirects to the start of the payment/verification flow. """ - url = reverse('verify_student_start_flow', kwargs={'course_id': six.text_type(course_id)}) + url = reverse('verify_student_start_flow', kwargs={'course_id': str(course_id)}) with mock_payment_processors(): self.assertRedirects(response, url) @@ -997,14 +995,14 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin, Tes def _assert_redirects_to_upgrade(self, response, course_id): """Check that the page redirects to the "upgrade" part of the flow. """ - url = reverse('verify_student_upgrade_and_verify', kwargs={'course_id': six.text_type(course_id)}) + url = reverse('verify_student_upgrade_and_verify', kwargs={'course_id': str(course_id)}) with mock_payment_processors(): self.assertRedirects(response, url) @ddt.data("verify_student_start_flow", "verify_student_begin_flow") def test_course_upgrade_page_with_unicode_and_special_values_in_display_name(self, payment_flow): """Check the course information on the page. """ - mode_display_name = u"Introduction à l'astrophysique" + mode_display_name = "Introduction à l'astrophysique" course = CourseFactory.create(display_name=mode_display_name) for course_mode in [CourseMode.DEFAULT_MODE_SLUG, "verified"]: min_price = (self.MIN_PRICE if course_mode != CourseMode.DEFAULT_MODE_SLUG else 0) @@ -1037,7 +1035,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin, Tes assert response.status_code == 200 -class CheckoutTestMixin(object): +class CheckoutTestMixin: """ Mixin implementing test methods that should behave identically regardless of which backend is used (currently only the ecommerce service). Subclasses @@ -1052,7 +1050,7 @@ class CheckoutTestMixin(object): def setUp(self): """ Create a user and course. """ - super(CheckoutTestMixin, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create(username="test", password="test") self.course = CourseFactory.create() @@ -1094,7 +1092,7 @@ class CheckoutTestMixin(object): def test_create_order(self, patched_create_order): # Create an order params = { - 'course_id': six.text_type(self.course.id), + 'course_id': str(self.course.id), 'contribution': 100, } self._assert_checked_out(params, patched_create_order, self.course.id, 'verified') @@ -1104,7 +1102,7 @@ class CheckoutTestMixin(object): course = CourseFactory.create() CourseModeFactory.create(mode_slug="professional", course_id=course.id, min_price=10, sku=self.make_sku()) # Create an order for a prof ed course - params = {'course_id': six.text_type(course.id)} + params = {'course_id': str(course.id)} self._assert_checked_out(params, patched_create_order, course.id, 'professional') def test_create_order_no_id_professional(self, patched_create_order): @@ -1112,7 +1110,7 @@ class CheckoutTestMixin(object): course = CourseFactory.create() CourseModeFactory.create(mode_slug="no-id-professional", course_id=course.id, min_price=10, sku=self.make_sku()) # Create an order for a prof ed course - params = {'course_id': six.text_type(course.id)} + params = {'course_id': str(course.id)} self._assert_checked_out(params, patched_create_order, course.id, 'no-id-professional') def test_create_order_for_multiple_paid_modes(self, patched_create_order): @@ -1121,14 +1119,14 @@ class CheckoutTestMixin(object): CourseModeFactory.create(mode_slug="no-id-professional", course_id=course.id, min_price=10, sku=self.make_sku()) CourseModeFactory.create(mode_slug="professional", course_id=course.id, min_price=10, sku=self.make_sku()) # Create an order for a prof ed course - params = {'course_id': six.text_type(course.id)} + params = {'course_id': str(course.id)} # TODO jsa - is this the intended behavior? self._assert_checked_out(params, patched_create_order, course.id, 'no-id-professional') def test_create_order_bad_donation_amount(self, patched_create_order): # Create an order params = { - 'course_id': six.text_type(self.course.id), + 'course_id': str(self.course.id), 'contribution': '99.9' } self._assert_checked_out(params, patched_create_order, None, None, expected_status_code=400) @@ -1136,7 +1134,7 @@ class CheckoutTestMixin(object): def test_create_order_good_donation_amount(self, patched_create_order): # Create an order params = { - 'course_id': six.text_type(self.course.id), + 'course_id': str(self.course.id), 'contribution': '100.0' } self._assert_checked_out(params, patched_create_order, self.course.id, 'verified') @@ -1149,7 +1147,7 @@ class CheckoutTestMixin(object): expected_payment_data['payment_form_data'].update({'foo': 'bar'}) patched_create_order.return_value = expected_payment_data # there is no 'processor' parameter in the post payload, so the response should only contain payment form data. - params = {'course_id': six.text_type(self.course.id), 'contribution': 100} + params = {'course_id': str(self.course.id), 'contribution': 100} response = self.client.post(reverse('verify_student_create_order'), params) assert response.status_code == 200 assert patched_create_order.called @@ -1174,7 +1172,7 @@ class TestCreateOrderEcommerceService(CheckoutTestMixin, ModuleStoreTestCase): def make_sku(self): """ Checkout is handled by the ecommerce service when the course mode's sku is nonempty. """ - return six.text_type(uuid4().hex) + return str(uuid4().hex) def _get_checkout_args(self, patched_create_order): """ Assuming patched_create_order was called, return a mapping containing the call arguments.""" @@ -1200,7 +1198,7 @@ class TestCheckoutWithEcommerceService(ModuleStoreTestCase): # mock out the payment processors endpoint httpretty.register_uri( httpretty.POST, - "{}/baskets/".format(TEST_API_URL), + f"{TEST_API_URL}/baskets/", body=json.dumps({'payment_data': expected_payment_data}), content_type="application/json", ) @@ -1233,10 +1231,10 @@ class TestSubmitPhotosForVerification(MockS3BotoMixin, TestVerificationBase): USERNAME = "test_user" PASSWORD = "test_password" IMAGE_DATA = "abcd,1234" - FULL_NAME = u"Ḟüḷḷ Ṅäṁë" + FULL_NAME = "Ḟüḷḷ Ṅäṁë" def setUp(self): - super(TestSubmitPhotosForVerification, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) result = self.client.login(username=self.USERNAME, password=self.PASSWORD) assert result, 'Could not log in' @@ -1447,7 +1445,7 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase, TestVerification """ def setUp(self): - super(TestPhotoVerificationResultsCallback, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course = CourseFactory.create(org='Robot', number='999', display_name='Test Course') self.course_id = self.course.id @@ -1591,7 +1589,7 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase, TestVerification ) attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=self.receipt_id) old_verification = SoftwareSecurePhotoVerification.objects.get(pk=verification.pk) - assert attempt.status == u'approved' + assert attempt.status == 'approved' assert attempt.expiration_datetime.date() == expiration_datetime.date() assert old_verification.expiry_email_date is None assert response.content.decode('utf-8') == 'OK!' @@ -1626,7 +1624,7 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase, TestVerification ) attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=self.receipt_id) - assert attempt.status == u'approved' + assert attempt.status == 'approved' assert attempt.expiration_datetime.date() == expiration_datetime.date() assert response.content.decode('utf-8') == 'OK!' self._assert_verification_approved_email(expiration_datetime.date()) @@ -1656,9 +1654,9 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase, TestVerification HTTP_DATE='testdate' ) attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=self.receipt_id) - assert attempt.status == u'denied' - assert attempt.error_code == u"Your photo doesn't meet standards." - assert attempt.error_msg == u'[{"photoIdReasons": ["Not provided"]}]' + assert attempt.status == 'denied' + assert attempt.error_code == "Your photo doesn't meet standards." + assert attempt.error_msg == '[{"photoIdReasons": ["Not provided"]}]' assert response.content.decode('utf-8') == 'OK!' self._assert_verification_denied_email() @@ -1683,9 +1681,9 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase, TestVerification HTTP_DATE='testdate' ) attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=self.receipt_id) - assert attempt.status == u'must_retry' - assert attempt.error_code == u'You must retry the verification.' - assert attempt.error_msg == u'"Memory overflow"' + assert attempt.status == 'must_retry' + assert attempt.error_code == 'You must retry the verification.' + assert attempt.error_msg == '"Memory overflow"' assert response.content.decode('utf-8') == 'OK!' @patch( @@ -1725,7 +1723,7 @@ class TestReverifyView(TestVerificationBase): PASSWORD = "detachment-2702" def setUp(self): - super(TestReverifyView, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) success = self.client.login(username=self.USERNAME, password=self.PASSWORD) assert success, 'Could not log in' @@ -1823,7 +1821,7 @@ class TestPhotoURLView(TestVerificationBase): """ def setUp(self): - super(TestPhotoURLView, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = AdminFactory() login_success = self.client.login(username=self.user.username, password='test') @@ -1836,7 +1834,7 @@ class TestPhotoURLView(TestVerificationBase): self.receipt_id = self.attempt.receipt_id def test_photo_url_view_returns_data(self): - url = reverse('verification_photo_urls', kwargs={'receipt_id': six.text_type(self.receipt_id)}) + url = reverse('verification_photo_urls', kwargs={'receipt_id': str(self.receipt_id)}) response = self.client.get(url) assert response.status_code == 200 assert response.data['EdX-ID'] == self.receipt_id @@ -1847,7 +1845,7 @@ class TestPhotoURLView(TestVerificationBase): def test_photo_url_view_returns_404_if_invalid_receipt_id(self): url = reverse('verification_photo_urls', - kwargs={'receipt_id': six.text_type('00000000-0000-0000-0000-000000000000')}) + kwargs={'receipt_id': '00000000-0000-0000-0000-000000000000'}) response = self.client.get(url) assert response.status_code == 404 @@ -1855,7 +1853,7 @@ class TestPhotoURLView(TestVerificationBase): self.user = UserFactory() login_success = self.client.login(username=self.user.username, password='test') assert login_success - url = reverse('verification_photo_urls', kwargs={'receipt_id': six.text_type(self.receipt_id)}) + url = reverse('verification_photo_urls', kwargs={'receipt_id': str(self.receipt_id)}) response = self.client.get(url) assert response.status_code == 403 @@ -1927,7 +1925,7 @@ class TestDecodeImageViews(MockS3BotoMixin, TestVerificationBase): url_name = 'verification_decrypt_face_image' if img_type == 'photo_id': url_name = 'verification_decrypt_photo_id_image' - url = reverse(url_name, kwargs={'receipt_id': six.text_type(receipt_id)}) + url = reverse(url_name, kwargs={'receipt_id': str(receipt_id)}) response = self.client.get(url) diff --git a/lms/djangoapps/verify_student/urls.py b/lms/djangoapps/verify_student/urls.py index db258bbeb4..7009a6ecd9 100644 --- a/lms/djangoapps/verify_student/urls.py +++ b/lms/djangoapps/verify_student/urls.py @@ -15,7 +15,7 @@ urlpatterns = [ # most likely after enrolling in a course and selecting # a "verified" track. url( - r'^start-flow/{course}/$'.format(course=settings.COURSE_ID_PATTERN), + fr'^start-flow/{settings.COURSE_ID_PATTERN}/$', # Pylint seems to dislike the as_view() method because as_view() is # decorated with `classonlymethod` instead of `classmethod`. views.PayAndVerifyView.as_view(), @@ -27,7 +27,7 @@ urlpatterns = [ # This is for A/B testing. url( - r'^begin-flow/{course}/$'.format(course=settings.COURSE_ID_PATTERN), + fr'^begin-flow/{settings.COURSE_ID_PATTERN}/$', views.PayAndVerifyView.as_view(), name="verify_student_begin_flow", kwargs={ @@ -39,7 +39,7 @@ urlpatterns = [ # This is the same as the "start verification" flow, # except with slight messaging changes. url( - r'^upgrade/{course}/$'.format(course=settings.COURSE_ID_PATTERN), + fr'^upgrade/{settings.COURSE_ID_PATTERN}/$', views.PayAndVerifyView.as_view(), name="verify_student_upgrade_and_verify", kwargs={ @@ -54,7 +54,7 @@ urlpatterns = [ # Note that if the user has already verified, this will redirect # to the dashboard. url( - r'^verify-now/{course}/$'.format(course=settings.COURSE_ID_PATTERN), + fr'^verify-now/{settings.COURSE_ID_PATTERN}/$', views.PayAndVerifyView.as_view(), name="verify_student_verify_now", kwargs={ @@ -102,19 +102,19 @@ urlpatterns = [ ), url( - r'^photo-urls/{receipt_id}$'.format(receipt_id=IDV_RECEIPT_ID_PATTERN), + fr'^photo-urls/{IDV_RECEIPT_ID_PATTERN}$', views.PhotoUrlsView.as_view(), name="verification_photo_urls" ), url( - r'^decrypt-idv-images/face/{receipt_id}$'.format(receipt_id=IDV_RECEIPT_ID_PATTERN), + fr'^decrypt-idv-images/face/{IDV_RECEIPT_ID_PATTERN}$', views.DecryptFaceImageView.as_view(), name="verification_decrypt_face_image" ), url( - r'^decrypt-idv-images/photo-id/{receipt_id}$'.format(receipt_id=IDV_RECEIPT_ID_PATTERN), + fr'^decrypt-idv-images/photo-id/{IDV_RECEIPT_ID_PATTERN}$', views.DecryptPhotoIDImageView.as_view(), name="verification_decrypt_photo_id_image" ), diff --git a/lms/djangoapps/verify_student/utils.py b/lms/djangoapps/verify_student/utils.py index 5591b14952..41d4f1ec4b 100644 --- a/lms/djangoapps/verify_student/utils.py +++ b/lms/djangoapps/verify_student/utils.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Common Utilities for the verify_student application. """ @@ -9,8 +8,6 @@ import logging from django.conf import settings from django.utils.timezone import now -from six import text_type - from lms.djangoapps.verify_student.tasks import send_request_to_ss_for_user log = logging.getLogger(__name__) @@ -29,7 +26,7 @@ def submit_request_to_ss(user_verification, copy_id_photo_from): ) except Exception as error: # pylint: disable=broad-except log.error( - "Software Secure submit request %r failed, result: %s", user_verification.user.username, text_type(error) + "Software Secure submit request %r failed, result: %s", user_verification.user.username, str(error) ) user_verification.mark_must_retry() diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index 8b9195238c..eb46811eb8 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -6,8 +6,8 @@ import datetime import decimal import json import logging +import urllib -import six from django.conf import settings from django.contrib.auth.decorators import login_required from django.contrib.staticfiles.storage import staticfiles_storage @@ -30,6 +30,11 @@ from rest_framework.views import APIView from common.djangoapps.course_modes.models import CourseMode from common.djangoapps.edxmako.shortcuts import render_to_response +from common.djangoapps.student.models import CourseEnrollment +from common.djangoapps.track import segment +from common.djangoapps.util.db import outer_atomic +from common.djangoapps.util.json_request import JsonResponse +from common.djangoapps.util.views import require_global_staff from lms.djangoapps.commerce.utils import EcommerceService, is_account_activation_requirement_disabled from lms.djangoapps.verify_student.emails import send_verification_approved_email, send_verification_confirmation_email from lms.djangoapps.verify_student.image import InvalidImageData, decode_image_data @@ -44,11 +49,6 @@ from openedx.core.djangoapps.user_api.accounts import NAME_MIN_LENGTH from openedx.core.djangoapps.user_api.accounts.api import update_account_settings from openedx.core.djangoapps.user_api.errors import AccountValidationError, UserNotFound from openedx.core.lib.log_utils import audit_log -from common.djangoapps.student.models import CourseEnrollment -from common.djangoapps.track import segment -from common.djangoapps.util.db import outer_atomic -from common.djangoapps.util.json_request import JsonResponse -from common.djangoapps.util.views import require_global_staff from xmodule.modulestore.django import modulestore from .services import IDVerificationService @@ -235,7 +235,7 @@ class PayAndVerifyView(View): # Verify that the course exists if course is None: - log.warning(u"Could not find course with ID %s.", course_id) + log.warning("Could not find course with ID %s.", course_id) raise Http404 # Check whether the user has access to this course @@ -262,7 +262,7 @@ class PayAndVerifyView(View): verification_deadline = VerificationDeadline.deadline_for_course(course.id) response = self._response_if_deadline_passed(course, self.VERIFICATION_DEADLINE, verification_deadline) if response is not None: - log.info(u"Verification deadline for '%s' has passed.", course.id) + log.info("Verification deadline for '%s' has passed.", course.id) return response # Retrieve the relevant course mode for the payment/verification flow. @@ -280,19 +280,19 @@ class PayAndVerifyView(View): if relevant_course_mode is not None: if CourseMode.is_verified_mode(relevant_course_mode): log.info( - u"Entering payment and verification flow for user '%s', course '%s', with current step '%s'.", + "Entering payment and verification flow for user '%s', course '%s', with current step '%s'.", request.user.id, course_id, current_step ) else: log.info( - u"Entering payment flow for user '%s', course '%s', with current step '%s'", + "Entering payment flow for user '%s', course '%s', with current step '%s'", request.user.id, course_id, current_step ) else: # Otherwise, there has never been a verified/paid mode, # so return a page not found response. log.warning( - u"No paid/verified course mode found for course '%s' for verification/payment flow request", + "No paid/verified course mode found for course '%s' for verification/payment flow request", course_id ) raise Http404 @@ -309,7 +309,7 @@ class PayAndVerifyView(View): upgrade_deadline = relevant_course_mode.expiration_datetime response = self._response_if_deadline_passed(course, self.UPGRADE_DEADLINE, upgrade_deadline) if response is not None: - log.info(u"Upgrade deadline for '%s' has passed.", course.id) + log.info("Upgrade deadline for '%s' has passed.", course.id) return response # Check whether the user has verified, paid, and enrolled. @@ -374,7 +374,7 @@ class PayAndVerifyView(View): if not course.start or course.start < now(): courseware_url = reverse( 'course_root', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ) full_name = ( @@ -387,7 +387,7 @@ class PayAndVerifyView(View): # use that amount to pre-fill the price selection form. contribution_amount = request.session.get( 'donation_for_course', {} - ).get(six.text_type(course_key), '') + ).get(str(course_key), '') # Remember whether the user is upgrading # so we can fire an analytics event upon payment. @@ -403,7 +403,7 @@ class PayAndVerifyView(View): context = { 'contribution_amount': contribution_amount, 'course': course, - 'course_key': six.text_type(course_key), + 'course_key': str(course_key), 'checkpoint_location': request.GET.get('checkpoint'), 'course_mode': relevant_course_mode, 'courseware_url': courseware_url, @@ -431,10 +431,10 @@ class PayAndVerifyView(View): # utm_params is [(u'utm_content', u'course-v1:IDBx IDB20.1x 1T2017'),... utm_params = [item for item in self.request.GET.items() if 'utm_' in item[0]] # utm_params is utm_content=course-v1%3AIDBx+IDB20.1x+1T2017&... - utm_params = six.moves.urllib.parse.urlencode(utm_params, True) + utm_params = urllib.parse.urlencode(utm_params, True) # utm_params is utm_content=course-v1:IDBx+IDB20.1x+1T2017&... # (course-keys do not have url encoding) - utm_params = six.moves.urllib.parse.unquote(utm_params) + utm_params = urllib.parse.unquote(utm_params) if utm_params: if '?' in url: url = url + '&' + utm_params @@ -478,7 +478,7 @@ class PayAndVerifyView(View): """ url = None - course_kwargs = {'course_id': six.text_type(course_key)} + course_kwargs = {'course_id': str(course_key)} if already_verified and already_paid: # If they've already paid and verified, there's nothing else to do, @@ -498,7 +498,7 @@ class PayAndVerifyView(View): if is_enrolled: if already_paid: # If the student has paid, but not verified, redirect to the verification flow. - url = IDVerificationService.get_verify_location(six.text_type(course_key)) + url = IDVerificationService.get_verify_location(str(course_key)) else: url = reverse('verify_student_start_flow', kwargs=course_kwargs) @@ -582,11 +582,11 @@ class PayAndVerifyView(View): else: # The "make payment" step doubles as an intro step, # so if we're showing the payment step, hide the intro step. - remove_steps |= set([self.INTRO_STEP]) + remove_steps |= {self.INTRO_STEP} return [ { 'name': step, - 'title': six.text_type(self.STEP_TITLES[step]), + 'title': str(self.STEP_TITLES[step]), } for step in display_steps if step not in remove_steps @@ -618,9 +618,9 @@ class PayAndVerifyView(View): if is_account_activation_requirement_disabled(): all_requirements.pop(self.ACCOUNT_ACTIVATION_REQ) - display_steps = set(step['name'] for step in display_steps) + display_steps = {step['name'] for step in display_steps} - for step, step_requirements in six.iteritems(self.STEP_REQUIREMENTS): + for step, step_requirements in self.STEP_REQUIREMENTS.items(): if step in display_steps: for requirement in step_requirements: all_requirements[requirement] = True @@ -700,7 +700,7 @@ class PayAndVerifyView(View): """ if deadline_name not in [self.VERIFICATION_DEADLINE, self.UPGRADE_DEADLINE]: - log.error(u"Invalid deadline name %s. Skipping check for whether the deadline passed.", deadline_name) + log.error("Invalid deadline name %s. Skipping check for whether the deadline passed.", deadline_name) return None deadline_passed = ( @@ -718,7 +718,7 @@ class PayAndVerifyView(View): def checkout_with_ecommerce_service(user, course_key, course_mode, processor): """ Create a new basket and trigger immediate checkout, using the E-Commerce API. """ - course_id = six.text_type(course_key) + course_id = str(course_key) try: api = ecommerce_api_client(user) # Make an API call to create the order and retrieve the results @@ -732,7 +732,7 @@ def checkout_with_ecommerce_service(user, course_key, course_mode, processor): return result.get('payment_data') except SlumberBaseException: params = {'username': user.username, 'mode': course_mode.slug, 'course_id': course_id} - log.exception(u'Failed to create order for %(username)s %(mode)s mode of %(course_id)s', params) + log.exception('Failed to create order for %(username)s %(mode)s mode of %(course_id)s', params) raise finally: audit_log( @@ -755,7 +755,7 @@ def create_order(request): course_id = request.POST['course_id'] course_id = CourseKey.from_string(course_id) donation_for_course = request.session.get('donation_for_course', {}) - contribution = request.POST.get("contribution", donation_for_course.get(six.text_type(course_id), 0)) + contribution = request.POST.get("contribution", donation_for_course.get(str(course_id), 0)) try: amount = decimal.Decimal(contribution).quantize(decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN) except decimal.InvalidOperation: @@ -768,7 +768,7 @@ def create_order(request): try: current_mode = CourseMode.objects.get(sku=sku) except CourseMode.DoesNotExist: - log.exception(u'Failed to find CourseMode with SKU [%s].', sku) + log.exception('Failed to find CourseMode with SKU [%s].', sku) if not current_mode: # Check if there are more than 1 paid(mode with min_price>0 e.g verified/professional/no-id-professional) modes @@ -776,12 +776,12 @@ def create_order(request): paid_modes = CourseMode.paid_modes_for_course(course_id) if paid_modes: if len(paid_modes) > 1: - log.warning(u"Multiple paid course modes found for course '%s' for create order request", course_id) + log.warning("Multiple paid course modes found for course '%s' for create order request", course_id) current_mode = paid_modes[0] # Make sure this course has a paid mode if not current_mode: - log.warning(u"Create order requested for course '%s' without a paid mode.", course_id) + log.warning("Create order requested for course '%s' without a paid mode.", course_id) return HttpResponseBadRequest(_("This course doesn't support paid certificates")) if CourseMode.is_professional_mode(current_mode): @@ -814,7 +814,7 @@ class SubmitPhotosView(View): @method_decorator(transaction.non_atomic_requests) def dispatch(self, request, *args, **kwargs): - return super(SubmitPhotosView, self).dispatch(request, *args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super().dispatch(request, *args, **kwargs) @method_decorator(login_required) @method_decorator(outer_atomic(read_committed=True)) @@ -902,7 +902,7 @@ class SubmitPhotosView(View): if "photo_id_image" not in params and not has_initial_verification: log.error( ( - u"User %s does not have an initial verification attempt " + "User %s does not have an initial verification attempt " "and no photo ID image data was provided. " "This most likely means that the JavaScript client is not " "correctly constructing the request to submit photos." @@ -915,7 +915,7 @@ class SubmitPhotosView(View): # The face image is always required. if "face_image" not in params: msg = _("Missing required parameter face_image") - log.error((u"User {user_id} missing required parameter face_image").format(user_id=request.user.id)) + log.error(("User {user_id} missing required parameter face_image").format(user_id=request.user.id)) return None, HttpResponseBadRequest(msg) return params, None @@ -935,14 +935,14 @@ class SubmitPhotosView(View): try: update_account_settings(request.user, {"name": full_name}) except UserNotFound: - log.error((u"No profile found for user {user_id}").format(user_id=request.user.id)) + log.error(("No profile found for user {user_id}").format(user_id=request.user.id)) return HttpResponseBadRequest(_("No profile found for user")) except AccountValidationError: msg = _( - u"Name must be at least {min_length} character long." + "Name must be at least {min_length} character long." ).format(min_length=NAME_MIN_LENGTH) log.error( - (u"User {user_id} provided an account name less than {min_length} characters").format( + ("User {user_id} provided an account name less than {min_length} characters").format( user_id=request.user.id, min_length=NAME_MIN_LENGTH ) @@ -977,7 +977,7 @@ class SubmitPhotosView(View): except InvalidImageData: msg = _("Image data is not valid.") - log.error((u"Image data for user {user_id} is not valid").format(user_id=request.user.id)) + log.error(("Image data for user {user_id} is not valid").format(user_id=request.user.id)) return None, None, HttpResponseBadRequest(msg) def _submit_attempt(self, user, face_image, photo_id_image=None, initial_verification=None): @@ -1053,12 +1053,12 @@ def results_callback(request): # lint-amnesty, pylint: disable=too-many-stateme try: body_dict = json.loads(body.decode('utf-8')) except ValueError: - log.exception(u"Invalid JSON received from Software Secure:\n\n{}\n".format(body)) - return HttpResponseBadRequest(u"Invalid JSON. Received:\n\n{}".format(body)) + log.exception(f"Invalid JSON received from Software Secure:\n\n{body}\n") + return HttpResponseBadRequest(f"Invalid JSON. Received:\n\n{body}") if not isinstance(body_dict, dict): - log.error(u"Reply from Software Secure is not a dict:\n\n{}\n".format(body)) - return HttpResponseBadRequest(u"JSON should be dict. Received:\n\n{}".format(body)) + log.error(f"Reply from Software Secure is not a dict:\n\n{body}\n") + return HttpResponseBadRequest(f"JSON should be dict. Received:\n\n{body}") headers = { "Authorization": request.META.get("HTTP_AUTHORIZATION", ""), @@ -1092,8 +1092,8 @@ def results_callback(request): # lint-amnesty, pylint: disable=too-many-stateme try: attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=receipt_id) except SoftwareSecurePhotoVerification.DoesNotExist: - log.error(u"Software Secure posted back for receipt_id %s, but not found", receipt_id) - return HttpResponseBadRequest(u"edX ID {} not found".format(receipt_id)) + log.error("Software Secure posted back for receipt_id %s, but not found", receipt_id) + return HttpResponseBadRequest(f"edX ID {receipt_id} not found") user = attempt.user verification_status_email_vars = { @@ -1106,7 +1106,7 @@ def results_callback(request): # lint-amnesty, pylint: disable=too-many-stateme if attempt.status != 'approved': verification = SoftwareSecurePhotoVerification.objects.filter(status='approved', user_id=attempt.user_id) if verification: - log.info(u'Making expiry email date of previous approved verification NULL for {}'.format(attempt.user_id)) # lint-amnesty, pylint: disable=line-too-long + log.info(f'Making expiry email date of previous approved verification NULL for {attempt.user_id}') # lint-amnesty, pylint: disable=line-too-long # The updated_at field in sspv model has auto_now set to True, which means any time save() is called on # the model instance, `updated_at` will change. Some of the existing functionality of verification # (showing your verification has expired on dashboard) relies on updated_at. @@ -1115,7 +1115,7 @@ def results_callback(request): # lint-amnesty, pylint: disable=too-many-stateme previous_verification = verification.latest('updated_at') SoftwareSecurePhotoVerification.objects.filter(pk=previous_verification.pk ).update(expiry_email_date=None) - log.debug(u'Approving verification for {}'.format(receipt_id)) + log.debug(f'Approving verification for {receipt_id}') attempt.approve() expiration_datetime = attempt.expiration_datetime.date() @@ -1123,13 +1123,13 @@ def results_callback(request): # lint-amnesty, pylint: disable=too-many-stateme send_verification_approved_email(context=email_context) elif result == "FAIL": - log.debug(u"Denying verification for %s", receipt_id) + log.debug("Denying verification for %s", receipt_id) attempt.deny(json.dumps(reason), error_code=error_code) - reverify_url = '{}/id-verification'.format(settings.ACCOUNT_MICROFRONTEND_URL) + reverify_url = f'{settings.ACCOUNT_MICROFRONTEND_URL}/id-verification' verification_status_email_vars['reasons'] = reason verification_status_email_vars['reverify_url'] = reverify_url verification_status_email_vars['faq_url'] = settings.ID_VERIFICATION_SUPPORT_LINK - subject = _(u"Your {platform_name} Verification Has Been Denied").format( + subject = _("Your {platform_name} Verification Has Been Denied").format( platform_name=settings.PLATFORM_NAME ) context = { @@ -1141,13 +1141,13 @@ def results_callback(request): # lint-amnesty, pylint: disable=too-many-stateme send_verification_status_email.delay(context) elif result == "SYSTEM FAIL": - log.debug(u"System failure for %s -- resetting to must_retry", receipt_id) + log.debug("System failure for %s -- resetting to must_retry", receipt_id) attempt.system_error(json.dumps(reason), error_code=error_code) - log.error(u"Software Secure callback attempt for %s failed: %s", receipt_id, reason) + log.error("Software Secure callback attempt for %s failed: %s", receipt_id, reason) else: - log.error(u"Software Secure returned unknown result %s", result) + log.error("Software Secure returned unknown result %s", result) return HttpResponseBadRequest( - u"Result {} not understood. Known results: PASS, FAIL, SYSTEM FAIL".format(result) + f"Result {result} not understood. Known results: PASS, FAIL, SYSTEM FAIL" ) return HttpResponse("OK!") @@ -1244,7 +1244,7 @@ class PhotoUrlsView(APIView): body.pop('SendResponseTo') return Response(body) - log.warning(u"Could not find verification with receipt ID %s.", receipt_id) + log.warning("Could not find verification with receipt ID %s.", receipt_id) raise Http404 @@ -1267,7 +1267,7 @@ class DecryptFaceImageView(APIView): """ # if this endpoint is not being accessed on stage, raise a 403. Only stage will have an RSA_PRIVATE_KEY if not settings.VERIFY_STUDENT["SOFTWARE_SECURE"].get("RSA_PRIVATE_KEY", None): - log.warning(u"Cannot access image decryption outside of staging environment") + log.warning("Cannot access image decryption outside of staging environment") return HttpResponseForbidden() verification = SoftwareSecurePhotoVerification.get_verification_from_receipt(receipt_id) @@ -1276,7 +1276,7 @@ class DecryptFaceImageView(APIView): if user_photo: return HttpResponse(user_photo, content_type="image/png") - log.warning(u"Could not decrypt face image for receipt ID %s.", receipt_id) + log.warning("Could not decrypt face image for receipt ID %s.", receipt_id) raise Http404 @@ -1299,7 +1299,7 @@ class DecryptPhotoIDImageView(APIView): """ # if this endpoint is not being accessed on stage, raise a 403. Only stage will have an RSA_PRIVATE_KEY if not settings.VERIFY_STUDENT["SOFTWARE_SECURE"].get("RSA_PRIVATE_KEY", None): - log.warning(u"Cannot access image decryption outside of staging environment") + log.warning("Cannot access image decryption outside of staging environment") return HttpResponseForbidden() verification = SoftwareSecurePhotoVerification.get_verification_from_receipt(receipt_id) @@ -1308,5 +1308,5 @@ class DecryptPhotoIDImageView(APIView): if id_photo: return HttpResponse(id_photo, content_type="image/png") - log.warning(u"Could not decrypt photo ID image for receipt ID %s.", receipt_id) + log.warning("Could not decrypt photo ID image for receipt ID %s.", receipt_id) raise Http404