pyupgrade on verify_students app (#26648)

This commit is contained in:
M. Zulqarnain
2021-03-02 16:45:01 +05:00
committed by GitHub
parent 29180d4b66
commit ba86198519
45 changed files with 309 additions and 352 deletions

View File

@@ -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
)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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__)

View File

@@ -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")

View File

@@ -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
})

View File

@@ -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",

View File

@@ -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',

View File

@@ -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'

View File

@@ -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')

View File

@@ -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'

View File

@@ -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)
),
)

View File

@@ -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__)

View File

@@ -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

View File

@@ -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')},
),
]

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models

View File

@@ -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',

View File

@@ -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)),
],
),

View File

@@ -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()),

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-11 19:15

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-27 16:27

View File

@@ -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)),
],
),

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.16 on 2019-01-10 09:19

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.26 on 2019-12-10 11:19

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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(

View File

@@ -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")

View File

@@ -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'

View File

@@ -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')
}

View File

@@ -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):
"""

View File

@@ -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),

View File

@@ -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

View File

@@ -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,

View File

@@ -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()

View File

@@ -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

View File

@@ -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,

View File

@@ -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)

View File

@@ -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"
),

View File

@@ -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()

View File

@@ -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