diff --git a/lms/djangoapps/instructor/access.py b/lms/djangoapps/instructor/access.py index 2619b7aaf0..ece469ea63 100644 --- a/lms/djangoapps/instructor/access.py +++ b/lms/djangoapps/instructor/access.py @@ -12,12 +12,15 @@ TO DO sync instructor and staff flags import logging +from common.djangoapps.student.roles import ( + CourseBetaTesterRole, + CourseCcxCoachRole, + CourseDataResearcherRole, + CourseInstructorRole, + CourseStaffRole +) from lms.djangoapps.instructor.enrollment import enroll_email, get_email_params from openedx.core.djangoapps.django_comment_common.models import Role -from common.djangoapps.student.roles import ( - CourseBetaTesterRole, CourseCcxCoachRole, CourseDataResearcherRole, - CourseInstructorRole, CourseStaffRole -) log = logging.getLogger(__name__) @@ -72,7 +75,7 @@ def _change_access(course, user, level, action, send_email=True): try: role = ROLES[level](course.id) except KeyError: - raise ValueError(u"unrecognized level '{}'".format(level)) # lint-amnesty, pylint: disable=raise-missing-from + raise ValueError(f"unrecognized level '{level}'") # lint-amnesty, pylint: disable=raise-missing-from if action == 'allow': if level == 'ccx_coach': @@ -88,7 +91,7 @@ def _change_access(course, user, level, action, send_email=True): elif action == 'revoke': role.remove_users(user) else: - raise ValueError(u"unrecognized action '{}'".format(action)) + raise ValueError(f"unrecognized action '{action}'") def update_forum_role(course_id, user, rolename, action): @@ -108,4 +111,4 @@ def update_forum_role(course_id, user, rolename, action): elif action == 'revoke': role.users.remove(user) else: - raise ValueError(u"unrecognized action '{}'".format(action)) + raise ValueError(f"unrecognized action '{action}'") diff --git a/lms/djangoapps/instructor/apps.py b/lms/djangoapps/instructor/apps.py index 5ed76baca4..6b7b05bd23 100644 --- a/lms/djangoapps/instructor/apps.py +++ b/lms/djangoapps/instructor/apps.py @@ -16,22 +16,22 @@ class InstructorConfig(AppConfig): """ Application Configuration for Instructor. """ - name = u'lms.djangoapps.instructor' + name = 'lms.djangoapps.instructor' plugin_app = { PluginURLs.CONFIG: { ProjectType.LMS: { - PluginURLs.NAMESPACE: u'', - PluginURLs.REGEX: u'^courses/{}/instructor/api/'.format(COURSE_ID_PATTERN), - PluginURLs.RELATIVE_PATH: u'views.api_urls', + PluginURLs.NAMESPACE: '', + PluginURLs.REGEX: f'^courses/{COURSE_ID_PATTERN}/instructor/api/', + PluginURLs.RELATIVE_PATH: 'views.api_urls', } }, PluginSettings.CONFIG: { ProjectType.LMS: { - SettingsType.DEVSTACK: {PluginSettings.RELATIVE_PATH: u'settings.devstack'}, - SettingsType.PRODUCTION: {PluginSettings.RELATIVE_PATH: u'settings.production'}, - SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: u'settings.common'}, - SettingsType.TEST: {PluginSettings.RELATIVE_PATH: u'settings.test'}, + SettingsType.DEVSTACK: {PluginSettings.RELATIVE_PATH: 'settings.devstack'}, + SettingsType.PRODUCTION: {PluginSettings.RELATIVE_PATH: 'settings.production'}, + SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: 'settings.common'}, + SettingsType.TEST: {PluginSettings.RELATIVE_PATH: 'settings.test'}, } } } diff --git a/lms/djangoapps/instructor/enrollment.py b/lms/djangoapps/instructor/enrollment.py index e010a8b4ac..53bf7da318 100644 --- a/lms/djangoapps/instructor/enrollment.py +++ b/lms/djangoapps/instructor/enrollment.py @@ -10,7 +10,6 @@ import logging from datetime import datetime import pytz -import six from django.conf import settings from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.core.mail import send_mail # lint-amnesty, pylint: disable=unused-import @@ -20,11 +19,21 @@ from django.utils.translation import override as override_language from edx_ace import ace from edx_ace.recipient import Recipient from eventtracking import tracker -from six import text_type from submissions import api as sub_api # installed from the edx-submissions repository from submissions.models import score_set from common.djangoapps.course_modes.models import CourseMode +from common.djangoapps.student.models import ( # lint-amnesty, pylint: disable=line-too-long + CourseEnrollment, + CourseEnrollmentAllowed, + anonymous_id_for_user, + is_email_retired +) +from common.djangoapps.track.event_transaction_utils import ( + create_new_event_transaction_id, + get_event_transaction_id, + set_event_transaction_type +) from lms.djangoapps.courseware.models import StudentModule from lms.djangoapps.grades.api import constants as grades_constants from lms.djangoapps.grades.api import disconnect_submissions_signal_receiver @@ -43,19 +52,13 @@ from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.user_api.models import UserPreference from openedx.core.djangolib.markup import Text -from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentAllowed, anonymous_id_for_user, is_email_retired # lint-amnesty, pylint: disable=line-too-long -from common.djangoapps.track.event_transaction_utils import ( - create_new_event_transaction_id, - get_event_transaction_id, - set_event_transaction_type -) from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import ItemNotFoundError log = logging.getLogger(__name__) -class EmailEnrollmentState(object): +class EmailEnrollmentState: """ Store the complete enrollment state of an email in a class """ def __init__(self, course_id, email): # N.B. retired users are not a concern here because they should be @@ -221,7 +224,7 @@ def send_beta_role_email(action, user, email_params): email_params['email_address'] = user.email email_params['full_name'] = user.profile.name else: - raise ValueError(u"Unexpected action received '{}' - expected 'add' or 'remove'".format(action)) + raise ValueError(f"Unexpected action received '{action}' - expected 'add' or 'remove'") trying_to_add_inactive_user = not user.is_active and action == 'add' if not trying_to_add_inactive_user: send_mail_to_student(user.email, email_params, language=get_user_email_language(user)) @@ -267,8 +270,8 @@ def reset_student_attempts(course_id, student, module_state_key, requesting_user with disconnect_submissions_signal_receiver(score_set): clear_student_state( user_id=user_id, - course_id=six.text_type(course_id), - item_id=six.text_type(module_state_key), + course_id=str(course_id), + item_id=str(module_state_key), requesting_user_id=requesting_user_id ) submission_cleared = True @@ -277,7 +280,7 @@ def reset_student_attempts(course_id, student, module_state_key, requesting_user selected_teamset_id = getattr(block, 'selected_teamset_id', None) except ItemNotFoundError: block = None - log.warning(u"Could not find %s in modulestore when attempting to reset attempts.", module_state_key) + log.warning("Could not find %s in modulestore when attempting to reset attempts.", module_state_key) # Reset the student's score in the submissions API, if xblock.clear_student_state has not done so already. # We need to do this before retrieving the `StudentModule` model, because a score may exist with no student module. @@ -287,8 +290,8 @@ def reset_student_attempts(course_id, student, module_state_key, requesting_user if delete_module and not submission_cleared: sub_api.reset_score( user_id, - text_type(course_id), - text_type(module_state_key), + str(course_id), + str(module_state_key), ) def _reset_or_delete_module(studentmodule): @@ -297,14 +300,14 @@ def reset_student_attempts(course_id, student, module_state_key, requesting_user create_new_event_transaction_id() set_event_transaction_type(grades_events.STATE_DELETED_EVENT_TYPE) tracker.emit( - six.text_type(grades_events.STATE_DELETED_EVENT_TYPE), + str(grades_events.STATE_DELETED_EVENT_TYPE), { - 'user_id': six.text_type(student.id), - 'course_id': six.text_type(course_id), - 'problem_id': six.text_type(module_state_key), - 'instructor_id': six.text_type(requesting_user.id), - 'event_transaction_id': six.text_type(get_event_transaction_id()), - 'event_transaction_type': six.text_type(grades_events.STATE_DELETED_EVENT_TYPE), + 'user_id': str(student.id), + 'course_id': str(course_id), + 'problem_id': str(module_state_key), + 'instructor_id': str(requesting_user.id), + 'event_transaction_id': str(get_event_transaction_id()), + 'event_transaction_type': str(grades_events.STATE_DELETED_EVENT_TYPE), } ) if not submission_cleared: @@ -376,8 +379,8 @@ def _fire_score_changed_for_block( raw_possible=max_score, weight=getattr(block, 'weight', None), user_id=student.id, - course_id=six.text_type(course_id), - usage_id=six.text_type(module_state_key), + course_id=str(course_id), + usage_id=str(module_state_key), score_deleted=True, only_if_higher=False, modified=datetime.now().replace(tzinfo=pytz.UTC), @@ -394,7 +397,7 @@ def get_email_params(course, auto_enroll, secure=True, course_key=None, display_ """ protocol = 'https' if secure else 'http' - course_key = course_key or text_type(course.id) + course_key = course_key or str(course.id) display_name = display_name or Text(course.display_name_with_default) stripped_site_name = configuration_helpers.get_value( @@ -403,12 +406,12 @@ def get_email_params(course, auto_enroll, secure=True, course_key=None, display_ ) # TODO: Use request.build_absolute_uri rather than '{proto}://{site}{path}'.format # and check with the Services team that this works well with microsites - registration_url = u'{proto}://{site}{path}'.format( + registration_url = '{proto}://{site}{path}'.format( proto=protocol, site=stripped_site_name, path=reverse('register_user') ) - course_url = u'{proto}://{site}{path}'.format( + course_url = '{proto}://{site}{path}'.format( proto=protocol, site=stripped_site_name, path=reverse('course_root', kwargs={'course_id': course_key}) @@ -417,7 +420,7 @@ def get_email_params(course, auto_enroll, secure=True, course_key=None, display_ # We can't get the url to the course's About page if the marketing site is enabled. course_about_url = None if not settings.FEATURES.get('ENABLE_MKTG_SITE', False): - course_about_url = u'{proto}://{site}{path}'.format( + course_about_url = '{proto}://{site}{path}'.format( proto=protocol, site=stripped_site_name, path=reverse('about_course', kwargs={'course_id': course_key}) diff --git a/lms/djangoapps/instructor/message_types.py b/lms/djangoapps/instructor/message_types.py index 6b14a4fe5d..6354cfb54f 100644 --- a/lms/djangoapps/instructor/message_types.py +++ b/lms/djangoapps/instructor/message_types.py @@ -15,7 +15,7 @@ class AccountCreationAndEnrollment(BaseMessageType): APP_LABEL = 'instructor' def __init__(self, *args, **kwargs): - super(AccountCreationAndEnrollment, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.options['transactional'] = True @@ -26,7 +26,7 @@ class AddBetaTester(BaseMessageType): APP_LABEL = 'instructor' def __init__(self, *args, **kwargs): - super(AddBetaTester, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.options['transactional'] = True @@ -37,7 +37,7 @@ class AllowedEnroll(BaseMessageType): APP_LABEL = 'instructor' def __init__(self, *args, **kwargs): - super(AllowedEnroll, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.options['transactional'] = True @@ -48,7 +48,7 @@ class AllowedUnenroll(BaseMessageType): APP_LABEL = 'instructor' def __init__(self, *args, **kwargs): - super(AllowedUnenroll, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.options['transactional'] = True @@ -59,7 +59,7 @@ class EnrollEnrolled(BaseMessageType): APP_LABEL = 'instructor' def __init__(self, *args, **kwargs): - super(EnrollEnrolled, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.options['transactional'] = True @@ -70,7 +70,7 @@ class EnrolledUnenroll(BaseMessageType): APP_LABEL = 'instructor' def __init__(self, *args, **kwargs): - super(EnrolledUnenroll, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.options['transactional'] = True @@ -81,5 +81,5 @@ class RemoveBetaTester(BaseMessageType): APP_LABEL = 'instructor' def __init__(self, *args, **kwargs): - super(RemoveBetaTester, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.options['transactional'] = True diff --git a/lms/djangoapps/instructor/permissions.py b/lms/djangoapps/instructor/permissions.py index e766dedc0b..20426761e6 100644 --- a/lms/djangoapps/instructor/permissions.py +++ b/lms/djangoapps/instructor/permissions.py @@ -4,8 +4,8 @@ Permissions for the instructor dashboard and associated actions from bridgekeeper import perms from bridgekeeper.rules import is_staff -from lms.djangoapps.courseware.rules import HasAccessRule, HasRolesRule +from lms.djangoapps.courseware.rules import HasAccessRule, HasRolesRule ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM = 'instructor.allow_student_to_bypass_entrance_exam' ASSIGN_TO_COHORTS = 'instructor.assign_to_cohorts' diff --git a/lms/djangoapps/instructor/services.py b/lms/djangoapps/instructor/services.py index 6d38ac6cf8..39c26605ee 100644 --- a/lms/djangoapps/instructor/services.py +++ b/lms/djangoapps/instructor/services.py @@ -12,17 +12,17 @@ from opaque_keys.edx.keys import CourseKey, UsageKey from opaque_keys.edx.locator import CourseLocator import lms.djangoapps.instructor.enrollment as enrollment -from lms.djangoapps.courseware.models import StudentModule -from lms.djangoapps.commerce.utils import create_zendesk_ticket -from lms.djangoapps.instructor.views.tools import get_student_from_identifier from common.djangoapps.student import auth from common.djangoapps.student.roles import CourseStaffRole +from lms.djangoapps.commerce.utils import create_zendesk_ticket +from lms.djangoapps.courseware.models import StudentModule +from lms.djangoapps.instructor.views.tools import get_student_from_identifier from xmodule.modulestore.django import modulestore log = logging.getLogger(__name__) -class InstructorService(object): +class InstructorService: """ Instructor service for deleting the students attempt(s) of an exam. This service has been created for the edx_proctoring's dependency injection to cater for a requirement where edx_proctoring @@ -49,7 +49,7 @@ class InstructorService(object): except ObjectDoesNotExist: err_msg = ( 'Error occurred while attempting to reset student attempts for user ' - u'{student_identifier} for content_id {content_id}. ' + '{student_identifier} for content_id {content_id}. ' 'User does not exist!'.format( student_identifier=student_identifier, content_id=content_id @@ -62,7 +62,7 @@ class InstructorService(object): module_state_key = UsageKey.from_string(content_id) except InvalidKeyError: err_msg = ( - u'Invalid content_id {content_id}!'.format(content_id=content_id) + f'Invalid content_id {content_id}!' ) log.error(err_msg) return @@ -79,7 +79,7 @@ class InstructorService(object): except (StudentModule.DoesNotExist, enrollment.sub_api.SubmissionError): err_msg = ( 'Error occurred while attempting to reset student attempts for user ' - u'{student_identifier} for content_id {content_id}.'.format( + '{student_identifier} for content_id {content_id}.'.format( student_identifier=student_identifier, content_id=content_id ) @@ -108,17 +108,17 @@ class InstructorService(object): if course.create_zendesk_tickets: requester_name = "edx-proctoring" email = "edx-proctoring@edx.org" - subject = _(u"Proctored Exam Review: {review_status}").format(review_status=review_status) + subject = _("Proctored Exam Review: {review_status}").format(review_status=review_status) body = _( - u"A proctored exam attempt for {exam_name} in {course_name} by username: {student_username} " - u"was reviewed as {review_status} by the proctored exam review provider.\n" - u"Review link: {review_url}" + "A proctored exam attempt for {exam_name} in {course_name} by username: {student_username} " + "was reviewed as {review_status} by the proctored exam review provider.\n" + "Review link: {review_url}" ).format( exam_name=exam_name, course_name=course.display_name, student_username=student_username, review_status=review_status, - review_url=review_url or u'not available', + review_url=review_url or 'not available', ) tags = ["proctoring"] create_zendesk_ticket(requester_name, email, subject, body, tags) diff --git a/lms/djangoapps/instructor/tests/test_access.py b/lms/djangoapps/instructor/tests/test_access.py index 7f83d4dcf8..961a4d7203 100644 --- a/lms/djangoapps/instructor/tests/test_access.py +++ b/lms/djangoapps/instructor/tests/test_access.py @@ -4,13 +4,12 @@ Test instructor.access import pytest -from six.moves import range +from common.djangoapps.student.roles import CourseBetaTesterRole, CourseCcxCoachRole, CourseStaffRole +from common.djangoapps.student.tests.factories import UserFactory from lms.djangoapps.instructor.access import allow_access, list_with_level, revoke_access, update_forum_role from openedx.core.djangoapps.ace_common.tests.mixins import EmailTemplateTagMixin from openedx.core.djangoapps.django_comment_common.models import FORUM_ROLE_MODERATOR, Role -from common.djangoapps.student.roles import CourseBetaTesterRole, CourseCcxCoachRole, CourseStaffRole -from common.djangoapps.student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -19,11 +18,11 @@ class TestInstructorAccessList(SharedModuleStoreTestCase): """ Test access listings. """ @classmethod def setUpClass(cls): - super(TestInstructorAccessList, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(TestInstructorAccessList, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.instructors = [UserFactory.create() for _ in range(4)] for user in self.instructors: allow_access(self.course, user, 'instructor') @@ -44,11 +43,11 @@ class TestInstructorAccessAllow(EmailTemplateTagMixin, SharedModuleStoreTestCase """ Test access allow. """ @classmethod def setUpClass(cls): - super(TestInstructorAccessAllow, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(TestInstructorAccessAllow, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course = CourseFactory.create() @@ -89,11 +88,11 @@ class TestInstructorAccessRevoke(SharedModuleStoreTestCase): """ Test access revoke. """ @classmethod def setUpClass(cls): - super(TestInstructorAccessRevoke, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(TestInstructorAccessRevoke, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.staff = [UserFactory.create() for _ in range(4)] for user in self.staff: allow_access(self.course, user, 'staff') @@ -128,11 +127,11 @@ class TestInstructorAccessForum(SharedModuleStoreTestCase): """ @classmethod def setUpClass(cls): - super(TestInstructorAccessForum, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(TestInstructorAccessForum, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.mod_role = Role.objects.create( course_id=self.course.id, name=FORUM_ROLE_MODERATOR diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index aac2719b33..5a74c5f81c 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -1,9 +1,6 @@ -# -*- coding: utf-8 -*- """ Unit tests for instructor.api methods. """ - - import datetime import functools import io @@ -11,6 +8,7 @@ import json import random import shutil import tempfile +from unittest.mock import Mock, NonCallableMock, patch import ddt import pytest @@ -24,20 +22,44 @@ from django.http import HttpRequest, HttpResponse from django.test import RequestFactory, TestCase from django.urls import reverse as django_reverse from django.utils.translation import ugettext as _ +from edx_toggles.toggles.testutils import \ + override_waffle_flag # lint-amnesty, pylint: disable=unused-import, wrong-import-order from edx_when.api import get_dates_for_course, get_overrides_for_user, set_date_for_block from freezegun import freeze_time -from mock import Mock, NonCallableMock, patch from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locator import UsageKey from pytz import UTC -from six import text_type, unichr -from six.moves import range, zip from testfixtures import LogCapture -from lms.djangoapps.bulk_email.models import BulkEmailFlag, CourseEmail, CourseEmailTemplate from common.djangoapps.course_modes.models import CourseMode from common.djangoapps.course_modes.tests.factories import CourseModeFactory -from edx_toggles.toggles.testutils import override_waffle_flag # lint-amnesty, pylint: disable=unused-import, wrong-import-order +from common.djangoapps.student.models import ( + ALLOWEDTOENROLL_TO_ENROLLED, + ALLOWEDTOENROLL_TO_UNENROLLED, + ENROLLED_TO_ENROLLED, + ENROLLED_TO_UNENROLLED, + UNENROLLED_TO_ALLOWEDTOENROLL, + UNENROLLED_TO_ENROLLED, + UNENROLLED_TO_UNENROLLED, + CourseEnrollment, + CourseEnrollmentAllowed, + ManualEnrollmentAudit, + NonExistentCourseError, + get_retired_email_by_email, + get_retired_username_by_username +) +from common.djangoapps.student.roles import ( # lint-amnesty, pylint: disable=unused-import + CourseBetaTesterRole, + CourseDataResearcherRole, + CourseFinanceAdminRole, + CourseInstructorRole, + CourseSalesAdminRole +) +from common.djangoapps.student.tests.factories import ( # lint-amnesty, pylint: disable=unused-import + AdminFactory, + UserFactory +) +from lms.djangoapps.bulk_email.models import BulkEmailFlag, CourseEmail, CourseEmailTemplate from lms.djangoapps.certificates.api import generate_user_certificates from lms.djangoapps.certificates.models import CertificateStatuses from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory @@ -73,29 +95,6 @@ from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin from openedx.core.lib.teams_config import TeamsConfig from openedx.core.lib.xblock_utils import grade_histogram from openedx.features.course_experience import RELATIVE_DATES_FLAG -from common.djangoapps.student.models import ( - ALLOWEDTOENROLL_TO_ENROLLED, - ALLOWEDTOENROLL_TO_UNENROLLED, - ENROLLED_TO_ENROLLED, - ENROLLED_TO_UNENROLLED, - UNENROLLED_TO_ALLOWEDTOENROLL, - UNENROLLED_TO_ENROLLED, - UNENROLLED_TO_UNENROLLED, - CourseEnrollment, - CourseEnrollmentAllowed, - ManualEnrollmentAudit, - NonExistentCourseError, - get_retired_email_by_email, - get_retired_username_by_username -) -from common.djangoapps.student.roles import ( # lint-amnesty, pylint: disable=unused-import - CourseBetaTesterRole, - CourseDataResearcherRole, - CourseFinanceAdminRole, - CourseInstructorRole, - CourseSalesAdminRole -) -from common.djangoapps.student.tests.factories import AdminFactory, UserFactory # lint-amnesty, pylint: disable=unused-import from xmodule.fields import Date from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase @@ -144,11 +143,11 @@ REPORTS_DATA = ( ) -INSTRUCTOR_GET_ENDPOINTS = set([ +INSTRUCTOR_GET_ENDPOINTS = { 'get_anon_ids', 'get_issued_certificates', -]) -INSTRUCTOR_POST_ENDPOINTS = set([ +} +INSTRUCTOR_POST_ENDPOINTS = { 'add_users_to_cohorts', 'bulk_beta_modify_access', 'calculate_grades_csv', @@ -183,7 +182,7 @@ INSTRUCTOR_POST_ENDPOINTS = set([ 'students_update_enrollment', 'update_forum_role_membership', 'override_problem_score', -]) +} def reverse(endpoint, args=None, kwargs=None, is_dashboard_endpoint=True): @@ -207,7 +206,7 @@ def reverse(endpoint, args=None, kwargs=None, is_dashboard_endpoint=True): if is_dashboard_endpoint and is_endpoint_declared is False: # Verify that all endpoints are declared so we can ensure they are # properly validated elsewhere. - raise ValueError(u"The endpoint {} must be declared in ENDPOINTS before use.".format(endpoint)) + raise ValueError(f"The endpoint {endpoint} must be declared in ENDPOINTS before use.") return django_reverse(endpoint, args=args, kwargs=kwargs) @@ -234,7 +233,7 @@ def view_alreadyrunningerror_unicode(request): """ A dummy view that raises an AlreadyRunningError exception with unicode message """ - raise AlreadyRunningError(u'Text with unicode chárácters') + raise AlreadyRunningError('Text with unicode chárácters') @common_exceptions_400 @@ -252,7 +251,7 @@ class TestCommonExceptions400(TestCase): """ def setUp(self): - super(TestCommonExceptions400, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.request = Mock(spec=HttpRequest) self.request.META = {} @@ -282,7 +281,7 @@ class TestCommonExceptions400(TestCase): resp = view_alreadyrunningerror_unicode(self.request) self.assertContains( resp, - u'Text with unicode chárácters', + 'Text with unicode chárácters', status_code=400, ) @@ -312,14 +311,14 @@ class TestEndpointHttpMethods(SharedModuleStoreTestCase, LoginEnrollmentTestCase """ Set up test course. """ - super(TestEndpointHttpMethods, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): """ Set up global staff role so authorization will not fail. """ - super(TestEndpointHttpMethods, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() global_user = GlobalStaffFactory() self.client.login(username=global_user.username, password='test') @@ -328,7 +327,7 @@ class TestEndpointHttpMethods(SharedModuleStoreTestCase, LoginEnrollmentTestCase """ Tests that POST endpoints are rejected with 405 when using GET. """ - url = reverse(data, kwargs={'course_id': text_type(self.course.id)}) + url = reverse(data, kwargs={'course_id': str(self.course.id)}) response = self.client.get(url) assert response.status_code == 405, \ @@ -339,7 +338,7 @@ class TestEndpointHttpMethods(SharedModuleStoreTestCase, LoginEnrollmentTestCase """ Tests that GET endpoints are not rejected with 405 when using GET. """ - url = reverse(data, kwargs={'course_id': text_type(self.course.id)}) + url = reverse(data, kwargs={'course_id': str(self.course.id)}) response = self.client.get(url) assert response.status_code != 405, \ @@ -354,7 +353,7 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest @classmethod def setUpClass(cls): - super(TestInstructorAPIDenyLevels, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() cls.chapter = ItemFactory.create( parent=cls.course, @@ -386,16 +385,16 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest publish_item=True, ) - cls.problem_urlname = text_type(cls.problem.location) + cls.problem_urlname = str(cls.problem.location) BulkEmailFlag.objects.create(enabled=True, require_course_email_auth=False) @classmethod def tearDownClass(cls): - super(TestInstructorAPIDenyLevels, cls).tearDownClass() + super().tearDownClass() BulkEmailFlag.objects.all().delete() def setUp(self): - super(TestInstructorAPIDenyLevels, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create() CourseEnrollment.enroll(self.user, self.course.id) @@ -460,7 +459,7 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest status_code: expected HTTP status code response msg: message to display if assertion fails. """ - url = reverse(endpoint, kwargs={'course_id': text_type(self.course.id)}) + url = reverse(endpoint, kwargs={'course_id': str(self.course.id)}) if endpoint in INSTRUCTOR_GET_ENDPOINTS: response = self.client.get(url, args) else: @@ -497,7 +496,7 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest msg: message to display if assertion fails. """ - mock_problem_key = NonCallableMock(return_value=u'') + mock_problem_key = NonCallableMock(return_value='') mock_problem_key.course_key = self.course.id with patch.object(UsageKey, 'from_string') as patched_method: patched_method.return_value = mock_problem_key @@ -587,7 +586,7 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas """ @classmethod def setUpClass(cls): - super(TestInstructorAPIBulkAccountCreationAndEnrollment, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() # Create a course with mode 'audit' @@ -595,14 +594,14 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas CourseModeFactory.create(course_id=cls.audit_course.id, mode_slug=CourseMode.AUDIT) cls.url = reverse( - 'register_and_enroll_students', kwargs={'course_id': text_type(cls.course.id)} + 'register_and_enroll_students', kwargs={'course_id': str(cls.course.id)} ) cls.audit_course_url = reverse( - 'register_and_enroll_students', kwargs={'course_id': text_type(cls.audit_course.id)} + 'register_and_enroll_students', kwargs={'course_id': str(cls.audit_course.id)} ) def setUp(self): - super(TestInstructorAPIBulkAccountCreationAndEnrollment, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # Create a course with mode 'honor' and with price self.white_label_course = CourseFactory.create() @@ -614,7 +613,7 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas ) self.white_label_course_url = reverse( - 'register_and_enroll_students', kwargs={'course_id': text_type(self.white_label_course.id)} + 'register_and_enroll_students', kwargs={'course_id': str(self.white_label_course.id)} ) self.request = RequestFactory().request() @@ -650,7 +649,7 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas assert manual_enrollments[0].state_transition == UNENROLLED_TO_ENROLLED # test the log for email that's send to new created user. - info_log.assert_called_with(u'email sent to new created user at %s', 'test_student@example.com') + info_log.assert_called_with('email sent to new created user at %s', 'test_student@example.com') @patch('lms.djangoapps.instructor.views.api.log.info') def test_account_creation_and_enrollment_with_csv_with_blank_lines(self, info_log): @@ -671,7 +670,7 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas assert manual_enrollments[0].state_transition == UNENROLLED_TO_ENROLLED # test the log for email that's send to new created user. - info_log.assert_called_with(u'email sent to new created user at %s', 'test_student@example.com') + info_log.assert_called_with('email sent to new created user at %s', 'test_student@example.com') @patch('lms.djangoapps.instructor.views.api.log.info') def test_email_and_username_already_exist(self, info_log): @@ -695,7 +694,7 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas # test the log for email that's send to new created user. info_log.assert_called_with( - u"user already exists with username '%s' and email '%s'", + "user already exists with username '%s' and email '%s'", 'test_student_1', 'test_student@example.com' ) @@ -760,7 +759,7 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas assert len(data['row_errors']) != 0 assert len(data['warnings']) == 0 assert len(data['general_errors']) == 0 - assert data['row_errors'][0]['response'] == u'Invalid email {0}.'.format('test_student.example.com') + assert data['row_errors'][0]['response'] == 'Invalid email {0}.'.format('test_student.example.com') manual_enrollments = ManualEnrollmentAudit.objects.all() assert manual_enrollments.count() == 0 @@ -776,8 +775,8 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas response = self.client.post(self.url, {'students_list': uploaded_file}) assert response.status_code == 200 info_log.assert_called_with( - u'user %s enrolled in the course %s', - u'NotEnrolledStudent', + 'user %s enrolled in the course %s', + 'NotEnrolledStudent', self.course.id ) manual_enrollments = ManualEnrollmentAudit.objects.all() @@ -796,8 +795,8 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas response = self.client.post(self.url, {'students_list': uploaded_file}) assert response.status_code == 200 data = json.loads(response.content.decode('utf-8')) - warning_message = u'An account with email {email} exists but the provided username {username} ' \ - u'is different. Enrolling anyway with {email}.'.format(email='test_student@example.com', username='test_student_2') # lint-amnesty, pylint: disable=line-too-long + warning_message = 'An account with email {email} exists but the provided username {username} ' \ + 'is different. Enrolling anyway with {email}.'.format(email='test_student@example.com', username='test_student_2') # lint-amnesty, pylint: disable=line-too-long assert len(data['warnings']) != 0 assert data['warnings'][0]['response'] == warning_message user = User.objects.get(email='test_student@example.com') @@ -830,7 +829,7 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas assert response.status_code == 200 data = json.loads(response.content.decode('utf-8')) assert len(data['row_errors']) != 0 - assert data['row_errors'][0]['response'] == u'Invalid email {email}.'.format(email=conflicting_email) + assert data['row_errors'][0]['response'] == f'Invalid email {conflicting_email}.' assert not User.objects.filter(email=conflicting_email).exists() def test_user_with_already_existing_username_in_csv(self): @@ -847,7 +846,7 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas assert response.status_code == 200 data = json.loads(response.content.decode('utf-8')) assert len(data['row_errors']) != 0 - assert data['row_errors'][0]['response'] == u'Username {user} already exists.'.format(user='test_student_1') + assert data['row_errors'][0]['response'] == 'Username {user} already exists.'.format(user='test_student_1') # lint-amnesty, pylint: disable=line-too-long def test_csv_file_not_attached(self): @@ -916,8 +915,8 @@ class TestInstructorAPIBulkAccountCreationAndEnrollment(SharedModuleStoreTestCas assert response.status_code == 200 data = json.loads(response.content.decode('utf-8')) assert len(data['row_errors']) != 0 - assert data['row_errors'][0]['response'] == u'Username {user} already exists.'.format(user='test_student_1') - assert data['row_errors'][1]['response'] == u'Invalid email {email}.'.format(email='test_student4@example.com') + assert data['row_errors'][0]['response'] == 'Username {user} already exists.'.format(user='test_student_1') + assert data['row_errors'][1]['response'] == 'Invalid email {email}.'.format(email='test_student4@example.com') assert User.objects.filter(username='test_student_1', email='test_student1@example.com').exists() assert User.objects.filter(username='test_student_2', email='test_student2@example.com').exists() assert not User.objects.filter(email='test_student3@example.com').exists() @@ -1015,7 +1014,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest @classmethod def setUpClass(cls): - super(TestInstructorAPIEnrollment, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() # Email URL values @@ -1023,11 +1022,11 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest 'SITE_NAME', settings.SITE_NAME ) - cls.about_path = '/courses/{}/about'.format(cls.course.id) - cls.course_path = '/courses/{}/'.format(cls.course.id) + cls.about_path = f'/courses/{cls.course.id}/about' + cls.course_path = f'/courses/{cls.course.id}/' def setUp(self): - super(TestInstructorAPIEnrollment, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.request = RequestFactory().request() self.instructor = InstructorFactory(course_key=self.course.id) @@ -1056,19 +1055,19 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest def test_missing_params(self): """ Test missing all query parameters. """ - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url) assert response.status_code == 400 def test_bad_action(self): """ Test with an invalid action. """ action = 'robot-not-an-action' - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.enrolled_student.email, 'action': action}) assert response.status_code == 400 def test_invalid_email(self): - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': 'percivaloctavius@', 'action': 'enroll', 'email_students': False}) # lint-amnesty, pylint: disable=line-too-long assert response.status_code == 200 @@ -1088,7 +1087,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest assert res_json == expected def test_invalid_username(self): - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': 'percivaloctavius', 'action': 'enroll', 'email_students': False}) assert response.status_code == 200 @@ -1109,7 +1108,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest assert res_json == expected def test_enroll_with_username(self): - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.notenrolled_student.username, 'action': 'enroll', 'email_students': False}) assert response.status_code == 200 @@ -1143,10 +1142,10 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest assert res_json == expected def test_enroll_without_email(self): - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.notenrolled_student.email, 'action': 'enroll', 'email_students': False}) - print(u"type(self.notenrolled_student.email): {}".format(type(self.notenrolled_student.email))) + print("type(self.notenrolled_student.email): {}".format(type(self.notenrolled_student.email))) assert response.status_code == 200 # test that the user is now enrolled @@ -1187,12 +1186,12 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest @ddt.data('http', 'https') def test_enroll_with_email(self, protocol): - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) params = {'identifiers': self.notenrolled_student.email, 'action': 'enroll', 'email_students': True} environ = {'wsgi.url_scheme': protocol} response = self.client.post(url, params, **environ) - print(u"type(self.notenrolled_student.email): {}".format(type(self.notenrolled_student.email))) + print("type(self.notenrolled_student.email): {}".format(type(self.notenrolled_student.email))) assert response.status_code == 200 # test that the user is now enrolled @@ -1227,7 +1226,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest # Check the outbox assert len(mail.outbox) == 1 - assert mail.outbox[0].subject == u'You have been enrolled in {}'.format(self.course.display_name) + assert mail.outbox[0].subject == f'You have been enrolled in {self.course.display_name}' text_body = mail.outbox[0].body html_body = mail.outbox[0].alternatives[0][0] @@ -1246,7 +1245,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest @ddt.data('http', 'https') def test_enroll_with_email_not_registered(self, protocol): - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) params = {'identifiers': self.notregistered_email, 'action': 'enroll', 'email_students': True} environ = {'wsgi.url_scheme': protocol} response = self.client.post(url, params, **environ) @@ -1257,21 +1256,21 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest # Check the outbox assert len(mail.outbox) == 1 - assert mail.outbox[0].subject == u'You have been invited to register for {}'.format(self.course.display_name) + assert mail.outbox[0].subject == f'You have been invited to register for {self.course.display_name}' text_body = mail.outbox[0].body html_body = mail.outbox[0].alternatives[0][0] - register_url = '{proto}://{site}/register'.format(proto=protocol, site=self.site_name) + register_url = f'{protocol}://{self.site_name}/register' assert text_body.startswith('Dear student,') - assert u'To finish your registration, please visit {register_url}'.format( + assert 'To finish your registration, please visit {register_url}'.format( register_url=register_url, ) in text_body assert 'Please finish your registration and fill out' in html_body assert register_url in html_body for body in [text_body, html_body]: - assert u'You have been invited to join {course} at edx.org by a member of the course staff.'.format( + assert 'You have been invited to join {course} at edx.org by a member of the course staff.'.format( course=self.course.display_name ) in body @@ -1291,7 +1290,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest @ddt.data('http', 'https') @patch.dict(settings.FEATURES, {'ENABLE_MKTG_SITE': True}) def test_enroll_email_not_registered_mktgsite(self, protocol): - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) params = {'identifiers': self.notregistered_email, 'action': 'enroll', 'email_students': True} environ = {'wsgi.url_scheme': protocol} response = self.client.post(url, params, **environ) @@ -1309,7 +1308,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest assert 'Please finish your registration and fill' in html_body for body in [text_body, html_body]: - assert u'You have been invited to join {display_name} at edx.org by a member of the course staff.'.format( + assert 'You have been invited to join {display_name} at edx.org by a member of the course staff.'.format( display_name=self.course.display_name ) in body @@ -1321,7 +1320,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest assert ('fill out the registration form making sure to use ' 'robot-not-an-email-yet@robot.org in the Email field') in body - assert u'You can then enroll in {display_name}.'.format( + assert 'You can then enroll in {display_name}.'.format( display_name=self.course.display_name ) in body @@ -1329,17 +1328,17 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest @ddt.data('http', 'https') def test_enroll_with_email_not_registered_autoenroll(self, protocol): - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) params = {'identifiers': self.notregistered_email, 'action': 'enroll', 'email_students': True, 'auto_enroll': True} environ = {'wsgi.url_scheme': protocol} response = self.client.post(url, params, **environ) - print(u"type(self.notregistered_email): {}".format(type(self.notregistered_email))) + print("type(self.notregistered_email): {}".format(type(self.notregistered_email))) assert response.status_code == 200 # Check the outbox assert len(mail.outbox) == 1 - assert mail.outbox[0].subject == u'You have been invited to register for {}'.format(self.course.display_name) + assert mail.outbox[0].subject == f'You have been invited to register for {self.course.display_name}' manual_enrollments = ManualEnrollmentAudit.objects.all() assert manual_enrollments.count() == 1 assert manual_enrollments[0].state_transition == UNENROLLED_TO_ALLOWEDTOENROLL @@ -1352,7 +1351,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest ) assert text_body.startswith('Dear student,') - assert u'To finish your registration, please visit {register_url}'.format( + assert 'To finish your registration, please visit {register_url}'.format( register_url=register_url, ) in text_body assert 'Please finish your registration and fill out the registration' in html_body @@ -1360,7 +1359,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest assert register_url in html_body for body in [text_body, html_body]: - assert u'You have been invited to join {display_name} at edx.org by a member of the course staff.'.format( + assert 'You have been invited to join {display_name} at edx.org by a member of the course staff.'.format( display_name=self.course.display_name ) in body @@ -1368,18 +1367,18 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest 'out the registration form making sure to use robot-not-an-email-yet@robot.org ' 'in the Email field') in body - assert (u'Once you have registered and activated your account, ' - u'you will see {display_name} listed on your dashboard.').format( + assert ('Once you have registered and activated your account, ' + 'you will see {display_name} listed on your dashboard.').format( display_name=self.course.display_name ) in body assert 'This email was automatically sent from edx.org to robot-not-an-email-yet@robot.org' in body def test_unenroll_without_email(self): - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.enrolled_student.email, 'action': 'unenroll', 'email_students': False}) - print(u"type(self.enrolled_student.email): {}".format(type(self.enrolled_student.email))) + print("type(self.enrolled_student.email): {}".format(type(self.enrolled_student.email))) assert response.status_code == 200 # test that the user is now unenrolled @@ -1419,10 +1418,10 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest assert len(mail.outbox) == 0 def test_unenroll_with_email(self): - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.enrolled_student.email, 'action': 'unenroll', 'email_students': True}) - print(u"type(self.enrolled_student.email): {}".format(type(self.enrolled_student.email))) + print("type(self.enrolled_student.email): {}".format(type(self.enrolled_student.email))) assert response.status_code == 200 # test that the user is now unenrolled @@ -1460,8 +1459,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest # Check the outbox assert len(mail.outbox) == 1 - assert mail.outbox[0].subject ==\ - u'You have been unenrolled from {display_name}'.format(display_name=self.course.display_name) + assert mail.outbox[0].subject == f'You have been unenrolled from {self.course.display_name}' text_body = mail.outbox[0].body html_body = mail.outbox[0].alternatives[0][0] @@ -1469,7 +1467,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest assert text_body.startswith('Dear Enrolled Student') for body in [text_body, html_body]: - assert u'You have been unenrolled from {display_name} at edx.org by a member of the course staff.'.format( + assert 'You have been unenrolled from {display_name} at edx.org by a member of the course staff.'.format( display_name=self.course.display_name, ) in body @@ -1478,7 +1476,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest assert 'This email was automatically sent from edx.org to Enrolled Student' in body def test_unenroll_with_email_allowed_student(self): - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.allowed_email, 'action': 'unenroll', 'email_students': True}) print(u"type(self.allowed_email): {}".format(type(self.allowed_email))) @@ -1515,15 +1513,14 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest # Check the outbox assert len(mail.outbox) == 1 - assert mail.outbox[0].subject ==\ - u'You have been unenrolled from {display_name}'.format(display_name=self.course.display_name) + assert mail.outbox[0].subject == f'You have been unenrolled from {self.course.display_name}' text_body = mail.outbox[0].body html_body = mail.outbox[0].alternatives[0][0] assert text_body.startswith('Dear Student,') for body in [text_body, html_body]: - assert u'You have been unenrolled from the course {display_name} by a member of the course staff.'.format( + assert 'You have been unenrolled from the course {display_name} by a member of the course staff.'.format( display_name=self.course.display_name, ) in body @@ -1535,7 +1532,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest def test_enroll_with_email_not_registered_with_shib(self, protocol, mock_uses_shib): mock_uses_shib.return_value = True - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) params = {'identifiers': self.notregistered_email, 'action': 'enroll', 'email_students': True} environ = {'wsgi.url_scheme': protocol} response = self.client.post(url, params, **environ) @@ -1543,8 +1540,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest # Check the outbox assert len(mail.outbox) == 1 - assert mail.outbox[0].subject == u'You have been invited to register for {display_name}'\ - .format(display_name=self.course.display_name) + assert mail.outbox[0].subject == f'You have been invited to register for {self.course.display_name}' text_body = mail.outbox[0].body html_body = mail.outbox[0].alternatives[0][0] @@ -1554,14 +1550,14 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest about_path=self.about_path, ) assert text_body.startswith('Dear student,') - assert u'To access this course visit {course_url} and register for this course.'.format( + assert 'To access this course visit {course_url} and register for this course.'.format( course_url=course_url, ) in text_body assert 'To access this course visit it and register:' in html_body assert course_url in html_body for body in [text_body, html_body]: - assert u'You have been invited to join {display_name} at edx.org by a member of the course staff.'.format( + assert 'You have been invited to join {display_name} at edx.org by a member of the course staff.'.format( display_name=self.course.display_name, ) in body @@ -1573,7 +1569,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest # Try with marketing site enabled and shib on mock_uses_shib.return_value = True - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) # Try with marketing site enabled with patch.dict('django.conf.settings.FEATURES', {'ENABLE_MKTG_SITE': True}): response = self.client.post(url, {'identifiers': self.notregistered_email, 'action': 'enroll', @@ -1586,7 +1582,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest assert text_body.startswith('Dear student,') for body in [text_body, html_body]: - assert u'You have been invited to join {display_name} at edx.org by a member of the course staff.'.format( + assert 'You have been invited to join {display_name} at edx.org by a member of the course staff.'.format( display_name=self.course.display_name, ) in body @@ -1597,18 +1593,17 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest def test_enroll_with_email_not_registered_with_shib_autoenroll(self, protocol, mock_uses_shib): mock_uses_shib.return_value = True - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) params = {'identifiers': self.notregistered_email, 'action': 'enroll', 'email_students': True, 'auto_enroll': True} environ = {'wsgi.url_scheme': protocol} response = self.client.post(url, params, **environ) - print(u"type(self.notregistered_email): {}".format(type(self.notregistered_email))) + print("type(self.notregistered_email): {}".format(type(self.notregistered_email))) assert response.status_code == 200 # Check the outbox assert len(mail.outbox) == 1 - assert mail.outbox[0].subject ==\ - u'You have been invited to register for {display_name}'.format(display_name=self.course.display_name) + assert mail.outbox[0].subject == f'You have been invited to register for {self.course.display_name}' text_body = mail.outbox[0].body html_body = mail.outbox[0].alternatives[0][0] @@ -1618,11 +1613,11 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest assert text_body.startswith('Dear student,') assert course_url in html_body - assert u'To access this course visit {course_url} and login.'.format(course_url=course_url) in text_body + assert f'To access this course visit {course_url} and login.' in text_body assert 'To access this course click on the button below and login:' in html_body for body in [text_body, html_body]: - assert u'You have been invited to join {display_name} at edx.org by a member of the course staff.'.format( + assert 'You have been invited to join {display_name} at edx.org by a member of the course staff.'.format( display_name=self.course.display_name, ) in body @@ -1637,9 +1632,9 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest user=self.enrolled_student, course_id=self.course.id ) # make this enrollment "verified" - course_enrollment.mode = u'verified' + course_enrollment.mode = 'verified' course_enrollment.save() - assert course_enrollment.mode == u'verified' + assert course_enrollment.mode == 'verified' # now re-enroll the student through the instructor dash self._change_student_enrollment(self.enrolled_student, self.course, 'enroll') @@ -1651,7 +1646,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest manual_enrollments = ManualEnrollmentAudit.objects.all() assert manual_enrollments.count() == 1 assert manual_enrollments[0].state_transition == ENROLLED_TO_ENROLLED - assert course_enrollment.mode == u'verified' + assert course_enrollment.mode == 'verified' def create_paid_course(self): """ @@ -1667,7 +1662,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest test to unenroll allow to enroll user. """ paid_course = self.create_paid_course() - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(paid_course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(paid_course.id)}) params = {'identifiers': self.notregistered_email, 'action': 'enroll', 'email_students': False, 'auto_enroll': False, 'reason': 'testing..', 'role': 'Learner'} response = self.client.post(url, params) @@ -1678,7 +1673,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest # now registered the user UserFactory(email=self.notregistered_email) - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(paid_course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(paid_course.id)}) params = {'identifiers': self.notregistered_email, 'action': 'enroll', 'email_students': False, 'auto_enroll': False, 'reason': 'testing', 'role': 'Learner'} response = self.client.post(url, params) @@ -1722,7 +1717,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest ) assert course_enrollment.count() == 0 - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(paid_course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(paid_course.id)}) params = {'identifiers': self.notregistered_email, 'action': 'unenroll', 'email_students': False, 'auto_enroll': False, 'reason': 'testing', 'role': 'Learner'} @@ -1768,9 +1763,9 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest user=self.enrolled_student, course_id=self.course.id ) # upgrade enrollment - course_enrollment.mode = u'verified' + course_enrollment.mode = 'verified' course_enrollment.save() - assert course_enrollment.mode == u'verified' + assert course_enrollment.mode == 'verified' self._change_student_enrollment(self.enrolled_student, self.course, 'unenroll') @@ -1786,7 +1781,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest test that role and reason fields are persisted in the database """ paid_course = self.create_paid_course() - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(paid_course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(paid_course.id)}) params = {'identifiers': self.notregistered_email, 'action': 'enroll', 'email_students': False, 'auto_enroll': False, 'reason': 'testing', 'role': 'Learner'} response = self.client.post(url, params) @@ -1803,7 +1798,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest """ url = reverse( 'students_update_enrollment', - kwargs={'course_id': text_type(course.id)}, + kwargs={'course_id': str(course.id)}, ) params = { 'identifiers': user.email, @@ -1822,7 +1817,7 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest # enrolled, active url = reverse( 'get_student_enrollment_status', - kwargs={'course_id': text_type(self.course.id)}, + kwargs={'course_id': str(self.course.id)}, ) params = { 'unique_student_identifier': 'EnrolledStudent' @@ -1871,18 +1866,18 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll """ @classmethod def setUpClass(cls): - super(TestInstructorAPIBulkBetaEnrollment, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() # Email URL values cls.site_name = configuration_helpers.get_value( 'SITE_NAME', settings.SITE_NAME ) - cls.about_path = '/courses/{}/about'.format(cls.course.id) - cls.course_path = '/courses/{}/'.format(cls.course.id) + cls.about_path = f'/courses/{cls.course.id}/about' + cls.course_path = f'/courses/{cls.course.id}/' def setUp(self): - super(TestInstructorAPIBulkBetaEnrollment, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') @@ -1912,8 +1907,8 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll in which he/she is a beta-tester. """ with LogCapture() as capture: - message = u'Cancelling course certificate generation for user [{}] against course [{}], ' \ - u'user is a Beta Tester.' + message = 'Cancelling course certificate generation for user [{}] against course [{}], ' \ + 'user is a Beta Tester.' message = message.format(self.beta_tester.username, self.course.id) generate_user_certificates(self.beta_tester, self.course.id, self.course) @@ -1921,14 +1916,14 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll def test_missing_params(self): """ Test missing all query parameters. """ - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url) assert response.status_code == 400 def test_bad_action(self): """ Test with an invalid action. """ action = 'robot-not-an-action' - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.beta_tester.email, 'action': action}) assert response.status_code == 400 @@ -1965,32 +1960,32 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll assert len(mail.outbox) == 0 def test_add_notenrolled_email(self): - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.notenrolled_student.email, 'action': 'add', 'email_students': False}) # lint-amnesty, pylint: disable=line-too-long self.add_notenrolled(response, self.notenrolled_student.email) assert not CourseEnrollment.is_enrolled(self.notenrolled_student, self.course.id) def test_add_notenrolled_email_autoenroll(self): - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.notenrolled_student.email, 'action': 'add', 'email_students': False, 'auto_enroll': True}) # lint-amnesty, pylint: disable=line-too-long self.add_notenrolled(response, self.notenrolled_student.email) assert CourseEnrollment.is_enrolled(self.notenrolled_student, self.course.id) def test_add_notenrolled_username(self): - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.notenrolled_student.username, 'action': 'add', 'email_students': False}) # lint-amnesty, pylint: disable=line-too-long self.add_notenrolled(response, self.notenrolled_student.username) assert not CourseEnrollment.is_enrolled(self.notenrolled_student, self.course.id) def test_add_notenrolled_username_autoenroll(self): - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.notenrolled_student.username, 'action': 'add', 'email_students': False, 'auto_enroll': True}) # lint-amnesty, pylint: disable=line-too-long self.add_notenrolled(response, self.notenrolled_student.username) assert CourseEnrollment.is_enrolled(self.notenrolled_student, self.course.id) @ddt.data('http', 'https') def test_add_notenrolled_with_email(self, protocol): - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) params = {'identifiers': self.notenrolled_student.email, 'action': 'add', 'email_students': True} environ = {'wsgi.url_scheme': protocol} response = self.client.post(url, params, **environ) @@ -2014,17 +2009,16 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll # Check the outbox assert len(mail.outbox) == 1 - assert mail.outbox[0].subject ==\ - u'You have been invited to a beta test for {display_name}'.format(display_name=self.course.display_name) + assert mail.outbox[0].subject == f'You have been invited to a beta test for {self.course.display_name}' text_body = mail.outbox[0].body html_body = mail.outbox[0].alternatives[0][0] student_name = self.notenrolled_student.profile.name - assert text_body.startswith(u'Dear {student_name}'.format(student_name=student_name)) - assert u'Visit {display_name}'.format(display_name=self.course.display_name) in html_body + assert text_body.startswith(f'Dear {student_name}') + assert f'Visit {self.course.display_name}' in html_body for body in [text_body, html_body]: - assert u'You have been invited to be a beta tester for {display_name} at edx.org'.format( + assert 'You have been invited to be a beta tester for {display_name} at edx.org'.format( display_name=self.course.display_name, ) in body @@ -2037,13 +2031,13 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll about_path=self.about_path, ) in body - assert u'This email was automatically sent from edx.org to {student_email}'.format( + assert 'This email was automatically sent from edx.org to {student_email}'.format( student_email=self.notenrolled_student.email, ) in body @ddt.data('http', 'https') def test_add_notenrolled_with_email_autoenroll(self, protocol): - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) params = {'identifiers': self.notenrolled_student.email, 'action': 'add', 'email_students': True, 'auto_enroll': True} environ = {'wsgi.url_scheme': protocol} @@ -2068,16 +2062,15 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll # Check the outbox assert len(mail.outbox) == 1 - assert mail.outbox[0].subject ==\ - u'You have been invited to a beta test for {display_name}'.format(display_name=self.course.display_name) + assert mail.outbox[0].subject == f'You have been invited to a beta test for {self.course.display_name}' text_body = mail.outbox[0].body html_body = mail.outbox[0].alternatives[0][0] student_name = self.notenrolled_student.profile.name - assert text_body.startswith(u'Dear {student_name}'.format(student_name=student_name)) + assert text_body.startswith(f'Dear {student_name}') for body in [text_body, html_body]: - assert u'You have been invited to be a beta tester for {display_name} at edx.org'.format( + assert 'You have been invited to be a beta tester for {display_name} at edx.org'.format( display_name=self.course.display_name, ) in body @@ -2090,14 +2083,14 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll course_path=self.course_path ) - assert u'This email was automatically sent from edx.org to {student_email}'.format( + assert 'This email was automatically sent from edx.org to {student_email}'.format( student_email=self.notenrolled_student.email, ) in body @patch.dict(settings.FEATURES, {'ENABLE_MKTG_SITE': True}) def test_add_notenrolled_email_mktgsite(self): # Try with marketing site enabled - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.notenrolled_student.email, 'action': 'add', 'email_students': True}) # lint-amnesty, pylint: disable=line-too-long assert response.status_code == 200 @@ -2105,23 +2098,23 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll text_body = mail.outbox[0].body html_body = mail.outbox[0].alternatives[0][0] student_name = self.notenrolled_student.profile.name - assert text_body.startswith(u'Dear {student_name}'.format(student_name=student_name)) + assert text_body.startswith(f'Dear {student_name}') for body in [text_body, html_body]: - assert u'You have been invited to be a beta tester for {display_name} at edx.org'.format( + assert 'You have been invited to be a beta tester for {display_name} at edx.org'.format( display_name=self.course.display_name, ) in body assert 'by a member of the course staff.' in body assert 'Visit edx.org' in body assert 'enroll in this course and begin the beta test' in body - assert u'This email was automatically sent from edx.org to {student_email}'.format( + assert 'This email was automatically sent from edx.org to {student_email}'.format( student_email=self.notenrolled_student.email, ) in body def test_enroll_with_email_not_registered(self): # User doesn't exist - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.notregistered_email, 'action': 'add', 'email_students': True, 'reason': 'testing'}) @@ -2145,7 +2138,7 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll assert len(mail.outbox) == 0 def test_remove_without_email(self): - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.beta_tester.email, 'action': 'remove', 'email_students': False, 'reason': 'testing'}) @@ -2176,7 +2169,7 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll assert len(mail.outbox) == 0 def test_remove_with_email(self): - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'identifiers': self.beta_tester.email, 'action': 'remove', 'email_students': True, 'reason': 'testing'}) @@ -2208,10 +2201,10 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll text_body = mail.outbox[0].body html_body = mail.outbox[0].alternatives[0][0] - assert text_body.startswith(u'Dear {name}'.format(name=self.beta_tester.profile.name)) + assert text_body.startswith(f'Dear {self.beta_tester.profile.name}') for body in [text_body, html_body]: - assert u'You have been removed as a beta tester for {display_name} at edx.org'.format( + assert 'You have been removed as a beta tester for {display_name} at edx.org'.format( display_name=self.course.display_name, ) in body @@ -2220,7 +2213,7 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll assert 'Your other courses have not been affected.' in body - assert u'This email was automatically sent from edx.org to {email_address}'.format( + assert 'This email was automatically sent from edx.org to {email_address}'.format( email_address=self.beta_tester.email, ) in body @@ -2238,11 +2231,11 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe """ @classmethod def setUpClass(cls): - super(TestInstructorAPILevelsAccess, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(TestInstructorAPILevelsAccess, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') @@ -2253,13 +2246,13 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe def test_modify_access_noparams(self): """ Test missing all query parameters. """ - url = reverse('modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url) assert response.status_code == 400 def test_modify_access_bad_action(self): """ Test with an invalid action parameter. """ - url = reverse('modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.other_staff.email, 'rolename': 'staff', @@ -2269,7 +2262,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe def test_modify_access_bad_role(self): """ Test with an invalid action parameter. """ - url = reverse('modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.other_staff.email, 'rolename': 'robot-not-a-roll', @@ -2278,7 +2271,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe assert response.status_code == 400 def test_modify_access_allow(self): - url = reverse('modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.other_user.email, 'rolename': 'staff', @@ -2287,7 +2280,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe assert response.status_code == 200 def test_modify_access_allow_with_uname(self): - url = reverse('modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.other_instructor.username, 'rolename': 'staff', @@ -2296,7 +2289,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe assert response.status_code == 200 def test_modify_access_revoke(self): - url = reverse('modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.other_staff.email, 'rolename': 'staff', @@ -2305,7 +2298,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe assert response.status_code == 200 def test_modify_access_revoke_with_username(self): - url = reverse('modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.other_staff.username, 'rolename': 'staff', @@ -2314,7 +2307,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe assert response.status_code == 200 def test_modify_access_with_fake_user(self): - url = reverse('modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': 'GandalfTheGrey', 'rolename': 'staff', @@ -2331,7 +2324,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe def test_modify_access_with_inactive_user(self): self.other_user.is_active = False self.other_user.save() - url = reverse('modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.other_user.username, 'rolename': 'beta', @@ -2347,7 +2340,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe def test_modify_access_revoke_not_allowed(self): """ Test revoking access that a user does not have. """ - url = reverse('modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.other_staff.email, 'rolename': 'instructor', @@ -2359,7 +2352,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe """ Test that an instructor cannot remove instructor privelages from themself. """ - url = reverse('modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('modify_access', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.instructor.email, 'rolename': 'instructor', @@ -2378,20 +2371,20 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe def test_list_course_role_members_noparams(self): """ Test missing all query parameters. """ - url = reverse('list_course_role_members', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_course_role_members', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url) assert response.status_code == 400 def test_list_course_role_members_bad_rolename(self): """ Test with an invalid rolename parameter. """ - url = reverse('list_course_role_members', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_course_role_members', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'rolename': 'robot-not-a-rolename', }) assert response.status_code == 400 def test_list_course_role_members_staff(self): - url = reverse('list_course_role_members', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_course_role_members', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'rolename': 'staff', }) @@ -2399,7 +2392,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe # check response content expected = { - 'course_id': text_type(self.course.id), + 'course_id': str(self.course.id), 'staff': [ { 'username': self.other_staff.username, @@ -2413,7 +2406,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe assert res_json == expected def test_list_course_role_members_beta(self): - url = reverse('list_course_role_members', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_course_role_members', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'rolename': 'beta', }) @@ -2421,7 +2414,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe # check response content expected = { - 'course_id': text_type(self.course.id), + 'course_id': str(self.course.id), 'beta': [] } res_json = json.loads(response.content.decode('utf-8')) @@ -2446,7 +2439,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe Test update forum role membership. Get unique_student_identifier, rolename and action and update forum role. """ - url = reverse('update_forum_role_membership', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('update_forum_role_membership', kwargs={'course_id': str(self.course.id)}) response = self.client.post( url, { @@ -2473,11 +2466,11 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment """ @classmethod def setUpClass(cls): - super(TestInstructorAPILevelsDataDump, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(TestInstructorAPILevelsDataDump, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course_mode = CourseMode(course_id=self.course.id, mode_slug="honor", mode_display_name="honor cert", @@ -2504,7 +2497,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment """ url = reverse( 'get_problem_responses', - kwargs={'course_id': text_type(self.course.id)} + kwargs={'course_id': str(self.course.id)} ) problem_location = '' @@ -2524,7 +2517,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment problem key that the get_problem_responses endpoint can work with. """ - mock_problem_key = NonCallableMock(return_value=u'') + mock_problem_key = NonCallableMock(return_value='') mock_problem_key.course_key = self.course.id with patch.object(UsageKey, 'from_string') as patched_method: patched_method.return_value = mock_problem_key @@ -2539,7 +2532,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment """ url = reverse( 'get_problem_responses', - kwargs={'course_id': text_type(self.course.id)} + kwargs={'course_id': str(self.course.id)} ) problem_location = '' @@ -2559,7 +2552,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment """ url = reverse( 'get_problem_responses', - kwargs={'course_id': text_type(self.course.id)} + kwargs={'course_id': str(self.course.id)} ) task_type = 'problem_responses_csv' already_running_status = generate_already_running_error_message(task_type) @@ -2576,9 +2569,9 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment correctly in the response to get_students_features. """ for student in self.students: - student.profile.city = u"Mos Eisley {}".format(student.id) + student.profile.city = f"Mos Eisley {student.id}" student.profile.save() - url = reverse('get_students_features', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('get_students_features', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {}) res_json = json.loads(response.content.decode('utf-8')) assert 'students' in res_json @@ -2598,7 +2591,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment Test that get_students_features includes cohort info when the course is cohorted, and does not when the course is not cohorted. """ - url = reverse('get_students_features', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('get_students_features', kwargs={'course_id': str(self.course.id)}) set_course_cohorted(self.course.id, is_cohorted) response = self.client.post(url, {}) @@ -2620,7 +2613,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment CourseDataResearcherRole(self.course.id).add_users(course_instructor) self.client.login(username=course_instructor.username, password='test') - url = reverse('get_students_features', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('get_students_features', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {}) res_json = json.loads(response.content.decode('utf-8')) @@ -2635,7 +2628,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment """ url = reverse( 'get_students_who_may_enroll', - kwargs={'course_id': text_type(self.course.id)} + kwargs={'course_id': str(self.course.id)} ) # Successful case: response = self.client.post(url, {}) @@ -2656,7 +2649,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment """ url = reverse( 'get_proctored_exam_results', - kwargs={'course_id': text_type(self.course.id)} + kwargs={'course_id': str(self.course.id)} ) # Successful case: @@ -2712,7 +2705,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment decorated_func = require_finance_admin(func) request = self.mock_request() CourseFinanceAdminRole(self.course.id).add_users(self.instructor) - decorated_func(request, text_type(self.course.id)) + decorated_func(request, str(self.course.id)) assert func.called @patch('lms.djangoapps.instructor.views.api.anonymous_id_for_user', Mock(return_value='42')) @@ -2722,7 +2715,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment Test the CSV output for the anonymized user ids. """ base_time = datetime.datetime.now(UTC) - url = reverse('get_anon_ids', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('get_anon_ids', kwargs={'course_id': str(self.course.id)}) with freeze_time(base_time): response = self.client.post(url, {}) @@ -2756,11 +2749,11 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment """ ex_status = 503 ex_reason = 'Slow Down' - url = reverse('list_report_downloads', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_report_downloads', kwargs={'course_id': str(self.course.id)}) with patch('storages.backends.s3boto.S3BotoStorage.listdir', side_effect=BotoServerError(ex_status, ex_reason)): response = self.client.post(url, {}) mock_error.assert_called_with( - u'Fetching files failed for course: %s, status: %s, reason: %s', + 'Fetching files failed for course: %s, status: %s, reason: %s', self.course.id, ex_status, ex_reason, @@ -2770,7 +2763,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment assert res_json == {'downloads': []} def test_list_report_downloads(self): - url = reverse('list_report_downloads', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_report_downloads', kwargs={'course_id': str(self.course.id)}) with patch('lms.djangoapps.instructor_task.models.DjangoStorageReportStore.links_for') as mock_links_for: mock_links_for.return_value = [ ('mock_file_name_1', 'https://1.mock.url'), @@ -2801,10 +2794,10 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment def test_calculate_report_csv_success( self, report_type, instructor_api_endpoint, task_api_endpoint, extra_instructor_api_kwargs ): - kwargs = {'course_id': text_type(self.course.id)} + kwargs = {'course_id': str(self.course.id)} kwargs.update(extra_instructor_api_kwargs) url = reverse(instructor_api_endpoint, kwargs=kwargs) - success_status = u"The {report_type} report is being created.".format(report_type=report_type) + success_status = f"The {report_type} report is being created." with patch(task_api_endpoint) as mock_task_api_endpoint: if report_type == 'problem responses': mock_task_api_endpoint.return_value = Mock(task_id='task-id-1138') @@ -2816,7 +2809,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment self.assertContains(response, success_status) def test_get_ora2_responses_success(self): - url = reverse('export_ora2_data', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('export_ora2_data', kwargs={'course_id': str(self.course.id)}) with patch('lms.djangoapps.instructor_task.api.submit_export_ora2_data') as mock_submit_ora2_task: mock_submit_ora2_task.return_value = True @@ -2825,7 +2818,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment self.assertContains(response, success_status) def test_get_ora2_responses_already_running(self): - url = reverse('export_ora2_data', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('export_ora2_data', kwargs={'course_id': str(self.course.id)}) task_type = 'export_ora2_data' already_running_status = generate_already_running_error_message(task_type) @@ -2836,7 +2829,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment self.assertContains(response, already_running_status, status_code=400) def test_get_ora2_submission_files_success(self): - url = reverse('export_ora2_submission_files', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('export_ora2_submission_files', kwargs={'course_id': str(self.course.id)}) with patch( 'lms.djangoapps.instructor_task.api.submit_export_ora2_submission_files' @@ -2849,7 +2842,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment self.assertContains(response, success_status) def test_get_ora2_submission_files_already_running(self): - url = reverse('export_ora2_submission_files', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('export_ora2_submission_files', kwargs={'course_id': str(self.course.id)}) task_type = 'export_ora2_submission_files' already_running_status = generate_already_running_error_message(task_type) @@ -2863,7 +2856,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment def test_get_student_progress_url(self): """ Test that progress_url is in the successful response. """ - url = reverse('get_student_progress_url', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('get_student_progress_url', kwargs={'course_id': str(self.course.id)}) data = {'unique_student_identifier': self.students[0].email} response = self.client.post(url, data) assert response.status_code == 200 @@ -2872,7 +2865,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment def test_get_student_progress_url_from_uname(self): """ Test that progress_url is in the successful response. """ - url = reverse('get_student_progress_url', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('get_student_progress_url', kwargs={'course_id': str(self.course.id)}) data = {'unique_student_identifier': self.students[0].username} response = self.client.post(url, data) assert response.status_code == 200 @@ -2881,13 +2874,13 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment def test_get_student_progress_url_noparams(self): """ Test that the endpoint 404's without the required query params. """ - url = reverse('get_student_progress_url', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('get_student_progress_url', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url) assert response.status_code == 400 def test_get_student_progress_url_nostudent(self): """ Test that the endpoint 400's when requesting an unknown email. """ - url = reverse('get_student_progress_url', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('get_student_progress_url', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url) assert response.status_code == 400 @@ -2902,16 +2895,16 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes """ @classmethod def setUpClass(cls): - super(TestInstructorAPIRegradeTask, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() cls.problem_location = msk_from_problem_urlname( cls.course.id, 'robot-some-problem-urlname' ) - cls.problem_urlname = text_type(cls.problem_location) + cls.problem_urlname = str(cls.problem_location) def setUp(self): - super(TestInstructorAPIRegradeTask, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') @@ -2927,7 +2920,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes def test_reset_student_attempts_deletall(self): """ Make sure no one can delete all students state on a problem. """ - url = reverse('reset_student_attempts', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('reset_student_attempts', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'problem_to_reset': self.problem_urlname, 'all_students': True, @@ -2937,7 +2930,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes def test_reset_student_attempts_single(self): """ Test reset single student attempts. """ - url = reverse('reset_student_attempts', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('reset_student_attempts', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'problem_to_reset': self.problem_urlname, 'unique_student_identifier': self.student.email, @@ -2951,7 +2944,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes @patch('lms.djangoapps.instructor_task.api.submit_reset_problem_attempts_for_all_students') def test_reset_student_attempts_all(self, act): """ Test reset all student attempts. """ - url = reverse('reset_student_attempts', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('reset_student_attempts', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'problem_to_reset': self.problem_urlname, 'all_students': True, @@ -2961,7 +2954,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes def test_reset_student_attempts_missingmodule(self): """ Test reset for non-existant problem. """ - url = reverse('reset_student_attempts', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('reset_student_attempts', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'problem_to_reset': 'robot-not-a-real-module', 'unique_student_identifier': self.student.email, @@ -2971,7 +2964,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes @patch('lms.djangoapps.grades.signals.handlers.PROBLEM_WEIGHTED_SCORE_CHANGED.send') def test_reset_student_attempts_delete(self, _mock_signal): """ Test delete single student state. """ - url = reverse('reset_student_attempts', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('reset_student_attempts', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'problem_to_reset': self.problem_urlname, 'unique_student_identifier': self.student.email, @@ -2984,7 +2977,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes def test_reset_student_attempts_nonsense(self): """ Test failure with both unique_student_identifier and all_students. """ - url = reverse('reset_student_attempts', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('reset_student_attempts', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'problem_to_reset': self.problem_urlname, 'unique_student_identifier': self.student.email, @@ -2995,7 +2988,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes @patch('lms.djangoapps.instructor_task.api.submit_rescore_problem_for_student') def test_rescore_problem_single(self, act): """ Test rescoring of a single student. """ - url = reverse('rescore_problem', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('rescore_problem', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'problem_to_reset': self.problem_urlname, 'unique_student_identifier': self.student.email, @@ -3006,7 +2999,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes @patch('lms.djangoapps.instructor_task.api.submit_rescore_problem_for_student') def test_rescore_problem_single_from_uname(self, act): """ Test rescoring of a single student. """ - url = reverse('rescore_problem', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('rescore_problem', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'problem_to_reset': self.problem_urlname, 'unique_student_identifier': self.student.username, @@ -3017,7 +3010,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes @patch('lms.djangoapps.instructor_task.api.submit_rescore_problem_for_all_students') def test_rescore_problem_all(self, act): """ Test rescoring for all students. """ - url = reverse('rescore_problem', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('rescore_problem', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'problem_to_reset': self.problem_urlname, 'all_students': True, @@ -3029,7 +3022,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes def test_course_has_entrance_exam_in_student_attempts_reset(self): """ Test course has entrance exam id set while resetting attempts""" url = reverse('reset_student_attempts_for_entrance_exam', - kwargs={'course_id': text_type(self.course.id)}) + kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'all_students': True, 'delete_module': False, @@ -3039,7 +3032,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes @patch.dict(settings.FEATURES, {'ENTRANCE_EXAMS': True}) def test_rescore_entrance_exam_with_invalid_exam(self): """ Test course has entrance exam id set while re-scoring. """ - url = reverse('rescore_entrance_exam', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('rescore_entrance_exam', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, }) @@ -3055,7 +3048,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE """ @classmethod def setUpClass(cls): - super(TestEntranceExamInstructorAPIRegradeTask, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create( org='test_org', course='test_course', @@ -3092,7 +3085,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE ) def setUp(self): - super(TestEntranceExamInstructorAPIRegradeTask, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.instructor = InstructorFactory(course_key=self.course.id) # Add instructor to invalid ee course @@ -3142,7 +3135,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_reset_entrance_exam_student_attempts_delete_all(self): """ Make sure no one can delete all students state on entrance exam. """ url = reverse('reset_student_attempts_for_entrance_exam', - kwargs={'course_id': text_type(self.course.id)}) + kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'all_students': True, 'delete_module': True, @@ -3152,7 +3145,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_reset_entrance_exam_student_attempts_single(self): """ Test reset single student attempts for entrance exam. """ url = reverse('reset_student_attempts_for_entrance_exam', - kwargs={'course_id': text_type(self.course.id)}) + kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, }) @@ -3167,7 +3160,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_reset_entrance_exam_all_student_attempts(self, act): """ Test reset all student attempts for entrance exam. """ url = reverse('reset_student_attempts_for_entrance_exam', - kwargs={'course_id': text_type(self.course.id)}) + kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'all_students': True, }) @@ -3177,7 +3170,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_reset_student_attempts_invalid_entrance_exam(self): """ Test reset for invalid entrance exam. """ url = reverse('reset_student_attempts_for_entrance_exam', - kwargs={'course_id': text_type(self.course_with_invalid_ee.id)}) + kwargs={'course_id': str(self.course_with_invalid_ee.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, }) @@ -3186,7 +3179,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_entrance_exam_student_delete_state(self): """ Test delete single student entrance exam state. """ url = reverse('reset_student_attempts_for_entrance_exam', - kwargs={'course_id': text_type(self.course.id)}) + kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, 'delete_module': True, @@ -3202,7 +3195,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE staff_user = StaffFactory(course_key=self.course.id) self.client.login(username=staff_user.username, password='test') url = reverse('reset_student_attempts_for_entrance_exam', - kwargs={'course_id': text_type(self.course.id)}) + kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, 'delete_module': True, @@ -3212,7 +3205,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_entrance_exam_reset_student_attempts_nonsense(self): """ Test failure with both unique_student_identifier and all_students. """ url = reverse('reset_student_attempts_for_entrance_exam', - kwargs={'course_id': text_type(self.course.id)}) + kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, 'all_students': True, @@ -3222,7 +3215,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE @patch('lms.djangoapps.instructor_task.api.submit_rescore_entrance_exam_for_student') def test_rescore_entrance_exam_single_student(self, act): """ Test re-scoring of entrance exam for single student. """ - url = reverse('rescore_entrance_exam', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('rescore_entrance_exam', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, }) @@ -3231,7 +3224,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_rescore_entrance_exam_all_student(self): """ Test rescoring for all students. """ - url = reverse('rescore_entrance_exam', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('rescore_entrance_exam', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'all_students': True, }) @@ -3239,7 +3232,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_rescore_entrance_exam_if_higher_all_student(self): """ Test rescoring for all students only if higher. """ - url = reverse('rescore_entrance_exam', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('rescore_entrance_exam', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'all_students': True, 'only_if_higher': True, @@ -3248,7 +3241,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_rescore_entrance_exam_all_student_and_single(self): """ Test re-scoring with both all students and single student parameters. """ - url = reverse('rescore_entrance_exam', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('rescore_entrance_exam', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, 'all_students': True, @@ -3257,7 +3250,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_rescore_entrance_exam_with_invalid_exam(self): """ Test re-scoring of entrance exam with invalid exam. """ - url = reverse('rescore_entrance_exam', kwargs={'course_id': text_type(self.course_with_invalid_ee.id)}) + url = reverse('rescore_entrance_exam', kwargs={'course_id': str(self.course_with_invalid_ee.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, }) @@ -3266,13 +3259,13 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_list_entrance_exam_instructor_tasks_student(self): """ Test list task history for entrance exam AND student. """ # create a re-score entrance exam task - url = reverse('rescore_entrance_exam', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('rescore_entrance_exam', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, }) assert response.status_code == 200 - url = reverse('list_entrance_exam_instructor_tasks', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_entrance_exam_instructor_tasks', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, }) @@ -3285,7 +3278,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_list_entrance_exam_instructor_tasks_all_student(self): """ Test list task history for entrance exam AND all student. """ - url = reverse('list_entrance_exam_instructor_tasks', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_entrance_exam_instructor_tasks', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {}) assert response.status_code == 200 @@ -3296,7 +3289,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_list_entrance_exam_instructor_with_invalid_exam_key(self): """ Test list task history for entrance exam failure if course has invalid exam. """ url = reverse('list_entrance_exam_instructor_tasks', - kwargs={'course_id': text_type(self.course_with_invalid_ee.id)}) + kwargs={'course_id': str(self.course_with_invalid_ee.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, }) @@ -3305,13 +3298,13 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE def test_skip_entrance_exam_student(self): """ Test skip entrance exam api for student. """ # create a re-score entrance exam task - url = reverse('mark_student_can_skip_entrance_exam', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('mark_student_can_skip_entrance_exam', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'unique_student_identifier': self.student.email, }) assert response.status_code == 200 # check response - message = _(u'This student (%s) will skip the entrance exam.') % self.student.email + message = _('This student (%s) will skip the entrance exam.') % self.student.email self.assertContains(response, message) # post again with same student @@ -3320,7 +3313,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE }) # This time response message should be different - message = _(u'This student (%s) is already allowed to skip the entrance exam.') % self.student.email + message = _('This student (%s) is already allowed to skip the entrance exam.') % self.student.email self.assertContains(response, message) @@ -3333,10 +3326,10 @@ class TestInstructorSendEmail(SiteMixin, SharedModuleStoreTestCase, LoginEnrollm """ @classmethod def setUpClass(cls): - super(TestInstructorSendEmail, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() - test_subject = u'\u1234 test subject' - test_message = u'\u6824 test message' + test_subject = '\u1234 test subject' + test_message = '\u6824 test message' cls.full_test_message = { 'send_to': '["myself", "staff"]', 'subject': test_subject, @@ -3346,23 +3339,23 @@ class TestInstructorSendEmail(SiteMixin, SharedModuleStoreTestCase, LoginEnrollm @classmethod def tearDownClass(cls): - super(TestInstructorSendEmail, cls).tearDownClass() + super().tearDownClass() BulkEmailFlag.objects.all().delete() def setUp(self): - super(TestInstructorSendEmail, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') def test_send_email_as_logged_in_instructor(self): - url = reverse('send_email', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('send_email', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, self.full_test_message) assert response.status_code == 200 def test_send_email_but_not_logged_in(self): self.client.logout() - url = reverse('send_email', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('send_email', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, self.full_test_message) assert response.status_code == 403 @@ -3370,7 +3363,7 @@ class TestInstructorSendEmail(SiteMixin, SharedModuleStoreTestCase, LoginEnrollm self.client.logout() student = UserFactory() self.client.login(username=student.username, password='test') - url = reverse('send_email', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('send_email', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, self.full_test_message) assert response.status_code == 403 @@ -3380,7 +3373,7 @@ class TestInstructorSendEmail(SiteMixin, SharedModuleStoreTestCase, LoginEnrollm assert response.status_code != 200 def test_send_email_no_sendto(self): - url = reverse('send_email', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('send_email', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'subject': 'test subject', 'message': 'test message', @@ -3388,7 +3381,7 @@ class TestInstructorSendEmail(SiteMixin, SharedModuleStoreTestCase, LoginEnrollm assert response.status_code == 400 def test_send_email_invalid_sendto(self): - url = reverse('send_email', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('send_email', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'send_to': '["invalid_target", "staff"]', 'subject': 'test subject', @@ -3397,7 +3390,7 @@ class TestInstructorSendEmail(SiteMixin, SharedModuleStoreTestCase, LoginEnrollm assert response.status_code == 400 def test_send_email_no_subject(self): - url = reverse('send_email', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('send_email', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'send_to': '["staff"]', 'message': 'test message', @@ -3405,7 +3398,7 @@ class TestInstructorSendEmail(SiteMixin, SharedModuleStoreTestCase, LoginEnrollm assert response.status_code == 400 def test_send_email_no_message(self): - url = reverse('send_email', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('send_email', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'send_to': '["staff"]', 'subject': 'test subject', @@ -3416,7 +3409,7 @@ class TestInstructorSendEmail(SiteMixin, SharedModuleStoreTestCase, LoginEnrollm site_email = self.site_configuration.site_values.get('course_email_from_addr') site_template = self.site_configuration.site_values.get('course_email_template_name') CourseEmailTemplate.objects.create(name=site_template) - url = reverse('send_email', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('send_email', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, self.full_test_message) assert response.status_code == 200 assert 1 == CourseEmail.objects.filter(course_id=self.course.id, sender=self.instructor, @@ -3433,7 +3426,7 @@ class TestInstructorSendEmail(SiteMixin, SharedModuleStoreTestCase, LoginEnrollm 'course_email_template_name': {self.course.id.org: org_template} }) self.site_configuration.save() - url = reverse('send_email', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('send_email', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, self.full_test_message) assert response.status_code == 200 assert 1 == CourseEmail.objects.filter(course_id=self.course.id, sender=self.instructor, @@ -3442,7 +3435,7 @@ class TestInstructorSendEmail(SiteMixin, SharedModuleStoreTestCase, LoginEnrollm template_name=org_template, from_addr=org_email).count() -class MockCompletionInfo(object): +class MockCompletionInfo: """Mock for get_task_completion_info""" times_called = 0 @@ -3459,7 +3452,7 @@ class TestInstructorAPITaskLists(SharedModuleStoreTestCase, LoginEnrollmentTestC Test instructor task list endpoint. """ - class FakeTask(object): + class FakeTask: """ Fake task object """ FEATURES = [ 'task_type', @@ -3504,7 +3497,7 @@ class TestInstructorAPITaskLists(SharedModuleStoreTestCase, LoginEnrollmentTestC @classmethod def setUpClass(cls): - super(TestInstructorAPITaskLists, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create( entrance_exam_id='i4x://{}/{}/chapter/Entrance_exam'.format('test_org', 'test_course') ) @@ -3512,10 +3505,10 @@ class TestInstructorAPITaskLists(SharedModuleStoreTestCase, LoginEnrollmentTestC cls.course.id, 'robot-some-problem-urlname' ) - cls.problem_urlname = text_type(cls.problem_location) + cls.problem_urlname = str(cls.problem_location) def setUp(self): - super(TestInstructorAPITaskLists, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') @@ -3536,7 +3529,7 @@ class TestInstructorAPITaskLists(SharedModuleStoreTestCase, LoginEnrollmentTestC def test_list_instructor_tasks_running(self, act): """ Test list of all running tasks. """ act.return_value = self.tasks - url = reverse('list_instructor_tasks', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_instructor_tasks', kwargs={'course_id': str(self.course.id)}) mock_factory = MockCompletionInfo() with patch( 'lms.djangoapps.instructor.views.instructor_task_helpers.get_task_completion_info' @@ -3557,7 +3550,7 @@ class TestInstructorAPITaskLists(SharedModuleStoreTestCase, LoginEnrollmentTestC def test_list_background_email_tasks(self, act): """Test list of background email tasks.""" act.return_value = self.tasks - url = reverse('list_background_email_tasks', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_background_email_tasks', kwargs={'course_id': str(self.course.id)}) mock_factory = MockCompletionInfo() with patch( 'lms.djangoapps.instructor.views.instructor_task_helpers.get_task_completion_info' @@ -3578,7 +3571,7 @@ class TestInstructorAPITaskLists(SharedModuleStoreTestCase, LoginEnrollmentTestC def test_list_instructor_tasks_problem(self, act): """ Test list task history for problem. """ act.return_value = self.tasks - url = reverse('list_instructor_tasks', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_instructor_tasks', kwargs={'course_id': str(self.course.id)}) mock_factory = MockCompletionInfo() with patch( 'lms.djangoapps.instructor.views.instructor_task_helpers.get_task_completion_info' @@ -3601,7 +3594,7 @@ class TestInstructorAPITaskLists(SharedModuleStoreTestCase, LoginEnrollmentTestC def test_list_instructor_tasks_problem_student(self, act): """ Test list task history for problem AND student. """ act.return_value = self.tasks - url = reverse('list_instructor_tasks', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_instructor_tasks', kwargs={'course_id': str(self.course.id)}) mock_factory = MockCompletionInfo() with patch( 'lms.djangoapps.instructor.views.instructor_task_helpers.get_task_completion_info' @@ -3630,11 +3623,11 @@ class TestInstructorEmailContentList(SharedModuleStoreTestCase, LoginEnrollmentT """ @classmethod def setUpClass(cls): - super(TestInstructorEmailContentList, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(TestInstructorEmailContentList, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') @@ -3664,7 +3657,7 @@ class TestInstructorEmailContentList(SharedModuleStoreTestCase, LoginEnrollmentT """ Calls the list_email_content endpoint and returns the repsonse """ self.setup_fake_email_info(num_emails, with_failures) task_history_request.return_value = list(self.tasks.values()) - url = reverse('list_email_content', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_email_content', kwargs={'course_id': str(self.course.id)}) with patch('lms.djangoapps.instructor.views.api.CourseEmail.objects.get') as mock_email_info: mock_email_info.side_effect = self.get_matching_mock_email response = self.client.post(url, {}) @@ -3696,7 +3689,7 @@ class TestInstructorEmailContentList(SharedModuleStoreTestCase, LoginEnrollmentT # Email content should be what's expected expected_message = self.emails[0].html_message returned_email_info = email_info[0] - received_message = returned_email_info[u'email'][u'html_message'] + received_message = returned_email_info['email']['html_message'] assert expected_message == received_message def test_content_list_no_emails(self, task_history_request): @@ -3717,7 +3710,7 @@ class TestInstructorEmailContentList(SharedModuleStoreTestCase, LoginEnrollmentT invalid_task = FakeContentTask(0, 0, 0, 'test') invalid_task.make_invalid_input() task_history_request.return_value = [invalid_task] - url = reverse('list_email_content', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_email_content', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {}) assert response.status_code == 200 @@ -3741,7 +3734,7 @@ class TestInstructorEmailContentList(SharedModuleStoreTestCase, LoginEnrollmentT email = FakeEmail(0) email_info = FakeEmailInfo(email, 0, 10) task_history_request.return_value = [task_info] - url = reverse('list_email_content', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('list_email_content', kwargs={'course_id': str(self.course.id)}) with patch('lms.djangoapps.instructor.views.api.CourseEmail.objects.get') as mock_email_info: mock_email_info.return_value = email response = self.client.post(url, {}) @@ -3772,16 +3765,16 @@ class TestInstructorAPIHelpers(TestCase): def test_split_input_list_unicode(self): assert _split_input_list('robot@robot.edu, robot2@robot.edu') == ['robot@robot.edu', 'robot2@robot.edu'] - assert _split_input_list(u'robot@robot.edu, robot2@robot.edu') == ['robot@robot.edu', 'robot2@robot.edu'] - assert _split_input_list(u'robot@robot.edu, robot2@robot.edu') == [u'robot@robot.edu', 'robot2@robot.edu'] - scary_unistuff = unichr(40960) + u'abcd' + unichr(1972) + assert _split_input_list('robot@robot.edu, robot2@robot.edu') == ['robot@robot.edu', 'robot2@robot.edu'] + assert _split_input_list('robot@robot.edu, robot2@robot.edu') == ['robot@robot.edu', 'robot2@robot.edu'] + scary_unistuff = chr(40960) + 'abcd' + chr(1972) assert _split_input_list(scary_unistuff) == [scary_unistuff] def test_msk_from_problem_urlname(self): course_id = CourseKey.from_string('MITx/6.002x/2013_Spring') name = 'L2Node1' output = 'i4x://MITx/6.002x/problem/L2Node1' - assert text_type(msk_from_problem_urlname(course_id, name)) == output + assert str(msk_from_problem_urlname(course_id, name)) == output def test_msk_from_problem_urlname_error(self): args = ('notagoodcourse', 'L2Node1') @@ -3794,10 +3787,10 @@ def get_extended_due(course, unit, user): Gets the overridden due date for the given user on the given unit. Returns `None` if there is no override set. """ - location = text_type(unit.location) + location = str(unit.location) dates = get_overrides_for_user(course.id, user) for override in dates: - if text_type(override['location']) == location: + if str(override['location']) == location: return override['actual_date'] return None @@ -3817,7 +3810,7 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase): """ @classmethod def setUpClass(cls): - super(TestDueDateExtensions, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() cls.due = datetime.datetime(2010, 5, 12, 2, 42, tzinfo=UTC) @@ -3826,21 +3819,21 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase): cls.week2 = ItemFactory.create(due=cls.due) cls.week3 = ItemFactory.create() # No due date cls.course.children = [ - text_type(cls.week1.location), - text_type(cls.week2.location), - text_type(cls.week3.location) + str(cls.week1.location), + str(cls.week2.location), + str(cls.week3.location) ] cls.homework = ItemFactory.create( parent_location=cls.week1.location, due=cls.due ) - cls.week1.children = [text_type(cls.homework.location)] + cls.week1.children = [str(cls.homework.location)] def setUp(self): """ Fixtures. """ - super(TestDueDateExtensions, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() user1 = UserFactory.create() StudentModule( @@ -3897,10 +3890,10 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase): extract_dates(None, self.course.id) def test_change_due_date(self): - url = reverse('change_due_date', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('change_due_date', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'student': self.user1.username, - 'url': text_type(self.week1.location), + 'url': str(self.week1.location), 'due_datetime': '12/30/2013 00:00' }) assert response.status_code == 200, response.content @@ -3908,20 +3901,20 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase): get_extended_due(self.course, self.week1, self.user1) def test_change_to_invalid_due_date(self): - url = reverse('change_due_date', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('change_due_date', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'student': self.user1.username, - 'url': text_type(self.week1.location), + 'url': str(self.week1.location), 'due_datetime': '01/01/2009 00:00' }) assert response.status_code == 400, response.content assert get_extended_due(self.course, self.week1, self.user1) is None def test_change_nonexistent_due_date(self): - url = reverse('change_due_date', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('change_due_date', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'student': self.user1.username, - 'url': text_type(self.week3.location), + 'url': str(self.week3.location), 'due_datetime': '12/30/2013 00:00' }) assert response.status_code == 400, response.content @@ -3930,10 +3923,10 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase): @override_experiment_waffle_flag(RELATIVE_DATES_FLAG, active=True) def test_reset_date(self): self.test_change_due_date() - url = reverse('reset_due_date', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('reset_due_date', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'student': self.user1.username, - 'url': text_type(self.week1.location), + 'url': str(self.week1.location), }) assert response.status_code == 200, response.content assert self.due == get_extended_due(self.course, self.week1, self.user1) @@ -3952,10 +3945,10 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase): assert get_date_for_block(self.course, self.week3, self.user1) == override # Now test that we noticed the edx-when date - url = reverse('reset_due_date', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('reset_due_date', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'student': self.user1.username, - 'url': text_type(self.week3.location), + 'url': str(self.week3.location), }) self.assertContains(response, 'Successfully reset due date for student') assert get_date_for_block(self.course, self.week3, self.user1) == original_due @@ -3963,25 +3956,25 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase): def test_show_unit_extensions(self): self.test_change_due_date() url = reverse('show_unit_extensions', - kwargs={'course_id': text_type(self.course.id)}) - response = self.client.post(url, {'url': text_type(self.week1.location)}) + kwargs={'course_id': str(self.course.id)}) + response = self.client.post(url, {'url': str(self.week1.location)}) assert response.status_code == 200, response.content assert json.loads(response.content.decode('utf-8')) ==\ - {u'data': [{u'Extended Due Date': u'2013-12-30 00:00', - u'Full Name': self.user1.profile.name, u'Username': self.user1.username}], - u'header': [u'Username', u'Full Name', u'Extended Due Date'], - u'title': (u'Users with due date extensions for %s' % self.week1.display_name)} + {u'data': [{'Extended Due Date': '2013-12-30 00:00', + 'Full Name': self.user1.profile.name, 'Username': self.user1.username}], + u'header': ['Username', 'Full Name', 'Extended Due Date'], + u'title': ('Users with due date extensions for %s' % self.week1.display_name)} def test_show_student_extensions(self): self.test_change_due_date() url = reverse('show_student_extensions', - kwargs={'course_id': text_type(self.course.id)}) + kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, {'student': self.user1.username}) assert response.status_code == 200, response.content assert json.loads(response.content.decode('utf-8')) ==\ - {u'data': [{u'Extended Due Date': u'2013-12-30 00:00', u'Unit': self.week1.display_name}], - u'header': [u'Unit', u'Extended Due Date'], - u'title': (u'Due date extensions for %s (%s)' % (self.user1.profile.name, self.user1.username))} + {'data': [{'Extended Due Date': '2013-12-30 00:00', 'Unit': self.week1.display_name}], + 'header': ['Unit', 'Extended Due Date'], + 'title': ('Due date extensions for %s (%s)' % (self.user1.profile.name, self.user1.username))} class TestDueDateExtensionsDeletedDate(ModuleStoreTestCase, LoginEnrollmentTestCase): @@ -3993,7 +3986,7 @@ class TestDueDateExtensionsDeletedDate(ModuleStoreTestCase, LoginEnrollmentTestC """ Fixtures. """ - super(TestDueDateExtensionsDeletedDate, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course = CourseFactory.create() self.due = datetime.datetime(2010, 5, 12, 2, 42, tzinfo=UTC) @@ -4003,15 +3996,15 @@ class TestDueDateExtensionsDeletedDate(ModuleStoreTestCase, LoginEnrollmentTestC self.week2 = ItemFactory.create(due=self.due) self.week3 = ItemFactory.create() # No due date self.course.children = [ - text_type(self.week1.location), - text_type(self.week2.location), - text_type(self.week3.location) + str(self.week1.location), + str(self.week2.location), + str(self.week3.location) ] self.homework = ItemFactory.create( parent_location=self.week1.location, due=self.due ) - self.week1.children = [text_type(self.homework.location)] + self.week1.children = [str(self.homework.location)] user1 = UserFactory.create() StudentModule( @@ -4074,10 +4067,10 @@ class TestDueDateExtensionsDeletedDate(ModuleStoreTestCase, LoginEnrollmentTestC due date, without causing an error. """ - url = reverse('change_due_date', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('change_due_date', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'student': self.user1.username, - 'url': text_type(self.week1.location), + 'url': str(self.week1.location), 'due_datetime': '12/30/2013 00:00' }) assert response.status_code == 200, response.content @@ -4088,10 +4081,10 @@ class TestDueDateExtensionsDeletedDate(ModuleStoreTestCase, LoginEnrollmentTestC self.week1 = self.store.update_item(self.week1, self.user1.id) extract_dates(None, self.course.id) # Now, week1's normal due date is deleted but the extension still exists. - url = reverse('reset_due_date', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('reset_due_date', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, { 'student': self.user1.username, - 'url': text_type(self.week1.location), + 'url': str(self.week1.location), }) assert response.status_code == 200, response.content assert self.due == get_extended_due(self.course, self.week1, self.user1) @@ -4103,11 +4096,11 @@ class TestCourseIssuedCertificatesData(SharedModuleStoreTestCase): """ @classmethod def setUpClass(cls): - super(TestCourseIssuedCertificatesData, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(TestCourseIssuedCertificatesData, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password='test') @@ -4127,7 +4120,7 @@ class TestCourseIssuedCertificatesData(SharedModuleStoreTestCase): """ Test certificates with status 'downloadable' should be in the response. """ - url = reverse('get_issued_certificates', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('get_issued_certificates', kwargs={'course_id': str(self.course.id)}) # firstly generating downloadable certificates with 'honor' mode certificate_count = 3 for __ in range(certificate_count): @@ -4149,7 +4142,7 @@ class TestCourseIssuedCertificatesData(SharedModuleStoreTestCase): """ Test for certificate csv features against mode. Certificates should be group by 'mode' in reponse. """ - url = reverse('get_issued_certificates', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('get_issued_certificates', kwargs={'course_id': str(self.course.id)}) # firstly generating downloadable certificates with 'honor' mode certificate_count = 3 for __ in range(certificate_count): @@ -4190,13 +4183,13 @@ class TestCourseIssuedCertificatesData(SharedModuleStoreTestCase): """ Test for certificate csv features. """ - url = reverse('get_issued_certificates', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('get_issued_certificates', kwargs={'course_id': str(self.course.id)}) # firstly generating downloadable certificates with 'honor' mode certificate_count = 3 for __ in range(certificate_count): self.generate_certificate(course_id=self.course.id, mode='honor', status=CertificateStatuses.downloadable) - current_date = datetime.date.today().strftime(u"%B %d, %Y") + current_date = datetime.date.today().strftime("%B %d, %Y") response = self.client.get(url, {'csv': 'true'}) assert response['Content-Type'] == 'text/csv' assert response['Content-Disposition'] == u'attachment; filename={0}'.format('issued_certificates.csv') @@ -4211,11 +4204,11 @@ class TestBulkCohorting(SharedModuleStoreTestCase): """ @classmethod def setUpClass(cls): - super(TestBulkCohorting, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(TestBulkCohorting, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.staff_user = StaffFactory(course_key=self.course.id) self.non_staff_user = UserFactory.create() self.tempdir = tempfile.mkdtemp() @@ -4229,8 +4222,8 @@ class TestBulkCohorting(SharedModuleStoreTestCase): __, file_name = tempfile.mkstemp(suffix=suffix, dir=self.tempdir) with open(file_name, 'w') as file_pointer: file_pointer.write(csv_data) - with open(file_name, 'r') as file_pointer: - url = reverse('add_users_to_cohorts', kwargs={'course_id': text_type(self.course.id)}) + with open(file_name) as file_pointer: + url = reverse('add_users_to_cohorts', kwargs={'course_id': str(self.course.id)}) return self.client.post(url, {'uploaded-file': file_pointer}) def expect_error_on_file_content(self, file_content, error, file_suffix='.csv'): diff --git a/lms/djangoapps/instructor/tests/test_api_email_localization.py b/lms/djangoapps/instructor/tests/test_api_email_localization.py index febed0910d..f25b0d026c 100644 --- a/lms/djangoapps/instructor/tests/test_api_email_localization.py +++ b/lms/djangoapps/instructor/tests/test_api_email_localization.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Unit tests for the localization of emails sent by instructor.api methods. """ @@ -7,13 +6,12 @@ Unit tests for the localization of emails sent by instructor.api methods. from django.core import mail from django.test.utils import override_settings from django.urls import reverse -from six import text_type +from common.djangoapps.student.models import CourseEnrollment +from common.djangoapps.student.tests.factories import UserFactory from lms.djangoapps.courseware.tests.factories import InstructorFactory from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY from openedx.core.djangoapps.user_api.preferences.api import delete_user_preference, set_user_preference -from common.djangoapps.student.models import CourseEnrollment -from common.djangoapps.student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -26,11 +24,11 @@ class TestInstructorAPIEnrollmentEmailLocalization(SharedModuleStoreTestCase): @classmethod def setUpClass(cls): - super(TestInstructorAPIEnrollmentEmailLocalization, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(TestInstructorAPIEnrollmentEmailLocalization, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # Platform language is English, instructor's language is Chinese, # student's language is Esperanto, so the emails should all be sent in @@ -46,7 +44,7 @@ class TestInstructorAPIEnrollmentEmailLocalization(SharedModuleStoreTestCase): """ Update the current student enrollment status. """ - url = reverse('students_update_enrollment', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('students_update_enrollment', kwargs={'course_id': str(self.course.id)}) args = {'identifiers': student_email, 'email_students': 'true', 'action': action, 'reason': 'testing'} response = self.client.post(url, args) return response @@ -56,7 +54,7 @@ class TestInstructorAPIEnrollmentEmailLocalization(SharedModuleStoreTestCase): Check that the email outbox contains exactly one message for which both the message subject and body contain a certain Esperanto string. """ - return self.check_outbox(u"Ýöü hävé ßéén") + return self.check_outbox("Ýöü hävé ßéén") def check_outbox(self, expected_message): """ @@ -82,7 +80,7 @@ class TestInstructorAPIEnrollmentEmailLocalization(SharedModuleStoreTestCase): self.check_outbox_is_esperanto() def test_set_beta_role(self): - url = reverse('bulk_beta_modify_access', kwargs={'course_id': text_type(self.course.id)}) + url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) self.client.post(url, {'identifiers': self.student.email, 'action': 'add', 'email_students': 'true'}) self.check_outbox_is_esperanto() diff --git a/lms/djangoapps/instructor/tests/test_certificates.py b/lms/djangoapps/instructor/tests/test_certificates.py index b3c642ae27..c60c81f7c6 100644 --- a/lms/djangoapps/instructor/tests/test_certificates.py +++ b/lms/djangoapps/instructor/tests/test_certificates.py @@ -5,11 +5,11 @@ import contextlib import io import json from datetime import datetime, timedelta -import pytest +from unittest import mock + import ddt -import mock +import pytest import pytz -import six from config_models.models import cache from django.conf import settings from django.core.exceptions import ObjectDoesNotExist @@ -19,7 +19,7 @@ from django.urls import reverse from capa.xqueue_interface import XQueueInterface from common.djangoapps.course_modes.models import CourseMode -from lms.djangoapps.courseware.tests.factories import GlobalStaffFactory, InstructorFactory, UserFactory +from common.djangoapps.student.models import CourseEnrollment from lms.djangoapps.certificates import api as certs_api from lms.djangoapps.certificates.models import ( CertificateGenerationConfiguration, @@ -33,10 +33,10 @@ from lms.djangoapps.certificates.tests.factories import ( CertificateWhitelistFactory, GeneratedCertificateFactory ) +from lms.djangoapps.courseware.tests.factories import GlobalStaffFactory, InstructorFactory, UserFactory from lms.djangoapps.grades.tests.utils import mock_passing_grade from lms.djangoapps.verify_student.services import IDVerificationService from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory -from common.djangoapps.student.models import CourseEnrollment from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -50,15 +50,15 @@ class CertificatesInstructorDashTest(SharedModuleStoreTestCase): @classmethod def setUpClass(cls): - super(CertificatesInstructorDashTest, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() cls.url = reverse( 'instructor_dashboard', - kwargs={'course_id': six.text_type(cls.course.id)} + kwargs={'course_id': str(cls.course.id)} ) def setUp(self): - super(CertificatesInstructorDashTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.global_staff = GlobalStaffFactory() self.instructor = InstructorFactory(course_key=self.course.id) @@ -187,7 +187,7 @@ class CertificatesInstructorDashTest(SharedModuleStoreTestCase): response = self.client.get(self.url) if expected_status == 'started': - expected = u'Generating example {name} certificate'.format(name=cert_name) + expected = f'Generating example {cert_name} certificate' self.assertContains(response, expected) elif expected_status == 'error': expected = self.ERROR_REASON @@ -196,7 +196,7 @@ class CertificatesInstructorDashTest(SharedModuleStoreTestCase): expected = self.DOWNLOAD_URL self.assertContains(response, expected) else: - self.fail(u"Invalid certificate status: {status}".format(status=expected_status)) + self.fail(f"Invalid certificate status: {expected_status}") def _assert_enable_certs_button_is_disabled(self): """Check that the "enable student-generated certificates" button is disabled. """ @@ -220,11 +220,11 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): """Tests for the certificates end-points in the instructor dash API. """ @classmethod def setUpClass(cls): - super(CertificatesInstructorApiTest, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(CertificatesInstructorApiTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.global_staff = GlobalStaffFactory() self.instructor = InstructorFactory(course_key=self.course.id) self.user = UserFactory() @@ -252,7 +252,7 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): self.client.login(username=self.global_staff.username, password='test') url = reverse( 'generate_example_certificates', - kwargs={'course_id': six.text_type(self.course.id)} + kwargs={'course_id': str(self.course.id)} ) response = self.client.post(url) @@ -270,7 +270,7 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): self.client.login(username=self.global_staff.username, password='test') url = reverse( 'enable_certificate_generation', - kwargs={'course_id': six.text_type(self.course.id)} + kwargs={'course_id': str(self.course.id)} ) params = {'certificates-enabled': 'true' if is_enabled else 'false'} response = self.client.post(url, data=params) @@ -286,7 +286,7 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): """Check that the response redirects to the certificates section. """ expected_redirect = reverse( 'instructor_dashboard', - kwargs={'course_id': six.text_type(self.course.id)} + kwargs={'course_id': str(self.course.id)} ) expected_redirect += '#view-certificates' self.assertRedirects(response, expected_redirect) @@ -300,7 +300,7 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): self.client.login(username=user.username, password='test') url = reverse( 'start_certificate_generation', - kwargs={'course_id': six.text_type(self.course.id)} + kwargs={'course_id': str(self.course.id)} ) response = self.client.post(url) @@ -318,7 +318,7 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): self.client.login(username=self.global_staff.username, password='test') url = reverse( 'start_certificate_generation', - kwargs={'course_id': six.text_type(self.course.id)} + kwargs={'course_id': str(self.course.id)} ) response = self.client.post(url) @@ -343,7 +343,7 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): # Login the client and access the url with 'certificate_statuses' self.client.login(username=self.global_staff.username, password='test') - url = reverse('start_certificate_regeneration', kwargs={'course_id': six.text_type(self.course.id)}) + url = reverse('start_certificate_regeneration', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, data={'certificate_statuses': [CertificateStatuses.downloadable]}) # Assert 200 status code in response @@ -355,8 +355,8 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): # Assert success message assert res_json['message'] ==\ - u'Certificate regeneration task has been started.' \ - u' You can view the status of the generation task in the "Pending Tasks" section.' + 'Certificate regeneration task has been started.' \ + ' You can view the status of the generation task in the "Pending Tasks" section.' @override_settings(AUDIT_CERT_CUTOFF_DATE=datetime.now(pytz.UTC) - timedelta(days=1)) @ddt.data( @@ -406,7 +406,7 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): self.client.login(username=self.global_staff.username, password='test') url = reverse( 'start_certificate_regeneration', - kwargs={'course_id': six.text_type(self.course.id)} + kwargs={'course_id': str(self.course.id)} ) with mock.patch.object(XQueueInterface, 'send_to_queue') as mock_send: @@ -425,8 +425,8 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): # Assert success message assert res_json['message'] ==\ - u'Certificate regeneration task has been started.' \ - u' You can view the status of the generation task in the "Pending Tasks" section.' + 'Certificate regeneration task has been started.' \ + ' You can view the status of the generation task in the "Pending Tasks" section.' # Now, check whether user has audit certificate. cert = certs_api.get_certificate_for_user(self.user.username, self.course.id) @@ -451,7 +451,7 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): # Login the client and access the url without 'certificate_statuses' self.client.login(username=self.global_staff.username, password='test') - url = reverse('start_certificate_regeneration', kwargs={'course_id': six.text_type(self.course.id)}) + url = reverse('start_certificate_regeneration', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url) # Assert 400 status code in response @@ -460,10 +460,10 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): # Assert Error Message assert res_json['message'] ==\ - u'Please select one or more certificate statuses that require certificate regeneration.' + 'Please select one or more certificate statuses that require certificate regeneration.' # Access the url passing 'certificate_statuses' that are not present in db - url = reverse('start_certificate_regeneration', kwargs={'course_id': six.text_type(self.course.id)}) + url = reverse('start_certificate_regeneration', kwargs={'course_id': str(self.course.id)}) response = self.client.post(url, data={'certificate_statuses': [CertificateStatuses.generating]}) # Assert 400 status code in response @@ -471,7 +471,7 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): res_json = json.loads(response.content.decode('utf-8')) # Assert Error Message - assert res_json['message'] == u'Please select certificate statuses from the list only.' + assert res_json['message'] == 'Please select certificate statuses from the list only.' @override_settings(CERT_QUEUE='certificates') @@ -480,18 +480,18 @@ class CertificateExceptionViewInstructorApiTest(SharedModuleStoreTestCase): """Tests for the generate certificates end-points in the instructor dash API. """ @classmethod def setUpClass(cls): - super(CertificateExceptionViewInstructorApiTest, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(CertificateExceptionViewInstructorApiTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.global_staff = GlobalStaffFactory() self.instructor = InstructorFactory(course_key=self.course.id) self.user = UserFactory() self.user2 = UserFactory() CourseEnrollment.enroll(self.user, self.course.id) CourseEnrollment.enroll(self.user2, self.course.id) - self.url = reverse('certificate_exception_view', kwargs={'course_id': six.text_type(self.course.id)}) + self.url = reverse('certificate_exception_view', kwargs={'course_id': str(self.course.id)}) certificate_white_list_item = CertificateWhitelistFactory.create( user=self.user2, @@ -503,7 +503,7 @@ class CertificateExceptionViewInstructorApiTest(SharedModuleStoreTestCase): notes="Test Notes for Test Certificate Exception", user_email='', user_id='', - user_name=six.text_type(self.user.username) + user_name=str(self.user.username) ) self.certificate_exception_in_db = dict( @@ -582,8 +582,8 @@ class CertificateExceptionViewInstructorApiTest(SharedModuleStoreTestCase): # Assert Error Message assert res_json['message'] ==\ - u'Student username/email field is required and can not be empty.' \ - u' Kindly fill in username/email and then press "Add to Exception List" button.' + 'Student username/email field is required and can not be empty.' \ + ' Kindly fill in username/email and then press "Add to Exception List" button.' def test_certificate_exception_duplicate_user_error(self): """ @@ -628,7 +628,7 @@ class CertificateExceptionViewInstructorApiTest(SharedModuleStoreTestCase): course2 = CourseFactory.create() url_course2 = reverse( 'certificate_exception_view', - kwargs={'course_id': six.text_type(course2.id)} + kwargs={'course_id': str(course2.id)} ) # add certificate exception for same user in a different course @@ -667,7 +667,7 @@ class CertificateExceptionViewInstructorApiTest(SharedModuleStoreTestCase): assert not res_json['success'] # Assert Error Message - assert res_json['message'] == u'{user} is not enrolled in this course. Please check your spelling and retry.'\ + assert res_json['message'] == '{user} is not enrolled in this course. Please check your spelling and retry.'\ .format(user=self.certificate_exception['user_name']) def test_certificate_exception_removed_successfully(self): @@ -718,7 +718,7 @@ class CertificateExceptionViewInstructorApiTest(SharedModuleStoreTestCase): assert not res_json['success'] # Assert Error Message assert res_json['message'] ==\ - u'The record is not in the correct format. Please add a valid username or email address.' + 'The record is not in the correct format. Please add a valid username or email address.' def test_remove_certificate_exception_non_existing_error(self): """ @@ -740,8 +740,8 @@ class CertificateExceptionViewInstructorApiTest(SharedModuleStoreTestCase): assert not res_json['success'] # Assert Error Message assert res_json['message'] ==\ - u'Certificate exception (user={user}) does not exist in certificate white list.' \ - u' Please refresh the page and try again.'.format(user=self.certificate_exception['user_name']) + 'Certificate exception (user={user}) does not exist in certificate white list.' \ + ' Please refresh the page and try again.'.format(user=self.certificate_exception['user_name']) @override_settings(CERT_QUEUE='certificates') @@ -750,11 +750,11 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase): """Tests for the generate certificates end-points in the instructor dash API. """ @classmethod def setUpClass(cls): - super(GenerateCertificatesInstructorApiTest, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() def setUp(self): - super(GenerateCertificatesInstructorApiTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.global_staff = GlobalStaffFactory() self.instructor = InstructorFactory(course_key=self.course.id) self.user = UserFactory() @@ -784,7 +784,7 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase): """ url = reverse( 'generate_certificate_exceptions', - kwargs={'course_id': six.text_type(self.course.id), 'generate_for': 'all'} + kwargs={'course_id': str(self.course.id), 'generate_for': 'all'} ) response = self.client.post( @@ -799,7 +799,7 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase): # Assert Request is successful assert res_json['success'] # Assert Message - assert res_json['message'] == u'Certificate generation started for white listed students.' + assert res_json['message'] == 'Certificate generation started for white listed students.' def test_generate_certificate_exceptions_whitelist_not_generated(self): """ @@ -808,7 +808,7 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase): """ url = reverse( 'generate_certificate_exceptions', - kwargs={'course_id': six.text_type(self.course.id), 'generate_for': 'new'} + kwargs={'course_id': str(self.course.id), 'generate_for': 'new'} ) response = self.client.post( @@ -824,7 +824,7 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase): # Assert Request is successful assert res_json['success'] # Assert Message - assert res_json['message'] == u'Certificate generation started for white listed students.' + assert res_json['message'] == 'Certificate generation started for white listed students.' def test_generate_certificate_exceptions_generate_for_incorrect_value(self): """ @@ -833,7 +833,7 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase): """ url = reverse( 'generate_certificate_exceptions', - kwargs={'course_id': six.text_type(self.course.id), 'generate_for': ''} + kwargs={'course_id': str(self.course.id), 'generate_for': ''} ) response = self.client.post( @@ -849,7 +849,7 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase): # Assert Request is not successful assert not res_json['success'] # Assert Message - assert res_json['message'] == u'Invalid data, generate_for must be "new" or "all".' + assert res_json['message'] == 'Invalid data, generate_for must be "new" or "all".' @ddt.ddt @@ -859,13 +859,13 @@ class TestCertificatesInstructorApiBulkWhiteListExceptions(SharedModuleStoreTest """ @classmethod def setUpClass(cls): - super(TestCertificatesInstructorApiBulkWhiteListExceptions, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() cls.url = reverse('generate_bulk_certificate_exceptions', kwargs={'course_id': cls.course.id}) def setUp(self): - super(TestCertificatesInstructorApiBulkWhiteListExceptions, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.global_staff = GlobalStaffFactory() self.enrolled_user_1 = UserFactory( username='TestStudent1', @@ -1016,14 +1016,14 @@ class CertificateInvalidationViewTests(SharedModuleStoreTestCase): """ @classmethod def setUpClass(cls): - super(CertificateInvalidationViewTests, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() cls.url = reverse('certificate_invalidation_view', kwargs={'course_id': cls.course.id}) cls.notes = "Test notes." def setUp(self): - super(CertificateInvalidationViewTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.global_staff = GlobalStaffFactory() self.enrolled_user_1 = UserFactory( username='TestStudent1', @@ -1115,8 +1115,8 @@ class CertificateInvalidationViewTests(SharedModuleStoreTestCase): # Assert Error Message assert res_json['message'] == \ - u'Student username/email field is required and can not be empty.' \ - u' Kindly fill in username/email and then press "Invalidate Certificate" button.' + 'Student username/email field is required and can not be empty.' \ + ' Kindly fill in username/email and then press "Invalidate Certificate" button.' def test_invalid_user_name_error(self): """ @@ -1156,7 +1156,7 @@ class CertificateInvalidationViewTests(SharedModuleStoreTestCase): res_json = json.loads(response.content.decode('utf-8')) # Assert Error Message - assert res_json['message'] == u'{user} is not enrolled in this course. Please check your spelling and retry.'\ + assert res_json['message'] == '{user} is not enrolled in this course. Please check your spelling and retry.'\ .format(user=self.not_enrolled_student.username) def test_no_generated_certificate_error(self): @@ -1176,7 +1176,7 @@ class CertificateInvalidationViewTests(SharedModuleStoreTestCase): res_json = json.loads(response.content.decode('utf-8')) # Assert Error Message - assert res_json['message'] == u'The student {student} does not have certificate for the course {course}. Kindly verify student username/email and the selected course are correct and try again.'.format(student=self.enrolled_user_2.username, course=self.course.number) # pylint: disable=line-too-long + assert res_json['message'] == 'The student {student} does not have certificate for the course {course}. Kindly verify student username/email and the selected course are correct and try again.'.format(student=self.enrolled_user_2.username, course=self.course.number) # pylint: disable=line-too-long def test_certificate_already_invalid_error(self): """ @@ -1196,7 +1196,7 @@ class CertificateInvalidationViewTests(SharedModuleStoreTestCase): res_json = json.loads(response.content.decode('utf-8')) # Assert Error Message - assert res_json['message'] == u'Certificate for student {user} is already invalid, kindly verify that certificate was generated for this student and then proceed.'.format(user=self.enrolled_user_1.username) # pylint: disable=line-too-long + assert res_json['message'] == 'Certificate for student {user} is already invalid, kindly verify that certificate was generated for this student and then proceed.'.format(user=self.enrolled_user_1.username) # pylint: disable=line-too-long def test_duplicate_certificate_invalidation_error(self): """ @@ -1220,7 +1220,7 @@ class CertificateInvalidationViewTests(SharedModuleStoreTestCase): res_json = json.loads(response.content.decode('utf-8')) # Assert Error Message - assert res_json['message'] == u'Certificate of {user} has already been invalidated. Please check your spelling and retry.'.format(user=self.enrolled_user_1.username) # pylint: disable=line-too-long + assert res_json['message'] == 'Certificate of {user} has already been invalidated. Please check your spelling and retry.'.format(user=self.enrolled_user_1.username) # pylint: disable=line-too-long def test_remove_certificate_invalidation(self): """ @@ -1271,4 +1271,4 @@ class CertificateInvalidationViewTests(SharedModuleStoreTestCase): res_json = json.loads(response.content.decode('utf-8')) # Assert Error Message - assert res_json['message'] == u'Certificate Invalidation does not exist, Please refresh the page and try again.' + assert res_json['message'] == 'Certificate Invalidation does not exist, Please refresh the page and try again.' diff --git a/lms/djangoapps/instructor/tests/test_email.py b/lms/djangoapps/instructor/tests/test_email.py index e990d8037b..1b2fc5e980 100644 --- a/lms/djangoapps/instructor/tests/test_email.py +++ b/lms/djangoapps/instructor/tests/test_email.py @@ -8,11 +8,10 @@ that the view is conditionally available when Course Auth is turned on. from django.urls import reverse from opaque_keys.edx.keys import CourseKey -from six import text_type +from common.djangoapps.student.tests.factories import AdminFactory from lms.djangoapps.bulk_email.api import is_bulk_email_enabled_for_course, is_bulk_email_feature_enabled from lms.djangoapps.bulk_email.models import BulkEmailFlag, CourseAuthorization -from common.djangoapps.student.tests.factories import AdminFactory from xmodule.modulestore.tests.django_utils import TEST_DATA_MIXED_MODULESTORE, SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -25,23 +24,23 @@ class TestNewInstructorDashboardEmailViewMongoBacked(SharedModuleStoreTestCase): @classmethod def setUpClass(cls): - super(TestNewInstructorDashboardEmailViewMongoBacked, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() # URL for instructor dash - cls.url = reverse('instructor_dashboard', kwargs={'course_id': text_type(cls.course.id)}) + cls.url = reverse('instructor_dashboard', kwargs={'course_id': str(cls.course.id)}) # URL for email view cls.email_link = '' def setUp(self): - super(TestNewInstructorDashboardEmailViewMongoBacked, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # Create instructor account instructor = AdminFactory.create() self.client.login(username=instructor.username, password="test") def tearDown(self): - super(TestNewInstructorDashboardEmailViewMongoBacked, self).tearDown() # lint-amnesty, pylint: disable=super-with-arguments + super().tearDown() BulkEmailFlag.objects.all().delete() # In order for bulk email to work, we must have both the BulkEmailFlag.is_enabled() @@ -119,28 +118,28 @@ class TestNewInstructorDashboardEmailViewXMLBacked(SharedModuleStoreTestCase): @classmethod def setUpClass(cls): - super(TestNewInstructorDashboardEmailViewXMLBacked, cls).setUpClass() + super().setUpClass() cls.course_key = CourseKey.from_string('edX/toy/2012_Fall') # URL for instructor dash - cls.url = reverse('instructor_dashboard', kwargs={'course_id': text_type(cls.course_key)}) + cls.url = reverse('instructor_dashboard', kwargs={'course_id': str(cls.course_key)}) # URL for email view cls.email_link = '' def setUp(self): - super(TestNewInstructorDashboardEmailViewXMLBacked, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # Create instructor account instructor = AdminFactory.create() self.client.login(username=instructor.username, password="test") # URL for instructor dash - self.url = reverse('instructor_dashboard', kwargs={'course_id': text_type(self.course_key)}) + self.url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course_key)}) # URL for email view self.email_link = '' def tearDown(self): - super(TestNewInstructorDashboardEmailViewXMLBacked, self).tearDown() # lint-amnesty, pylint: disable=super-with-arguments + super().tearDown() BulkEmailFlag.objects.all().delete() # The flag is enabled, and since REQUIRE_COURSE_EMAIL_AUTH is False, all courses should diff --git a/lms/djangoapps/instructor/tests/test_enrollment.py b/lms/djangoapps/instructor/tests/test_enrollment.py index c4aaf04c7d..d3f2cf018a 100644 --- a/lms/djangoapps/instructor/tests/test_enrollment.py +++ b/lms/djangoapps/instructor/tests/test_enrollment.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Unit tests for instructor.enrollment methods. """ @@ -6,25 +5,27 @@ Unit tests for instructor.enrollment methods. import json from abc import ABCMeta -import pytest +from unittest.mock import patch + import ddt -import six +import pytest from ccx_keys.locator import CCXLocator from crum import set_current_request from django.conf import settings from django.utils.translation import get_language from django.utils.translation import override as override_language -from mock import patch from opaque_keys.edx.locator import CourseLocator -from six import text_type from submissions import api as sub_api from capa.tests.response_xml_factory import MultipleChoiceResponseXMLFactory +from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentAllowed, anonymous_id_for_user +from common.djangoapps.student.roles import CourseCcxCoachRole +from common.djangoapps.student.tests.factories import AdminFactory, UserFactory +from lms.djangoapps.ccx.tests.factories import CcxFactory +from lms.djangoapps.course_blocks.api import get_course_blocks from lms.djangoapps.courseware.models import StudentModule from lms.djangoapps.grades.subsection_grade_factory import SubsectionGradeFactory from lms.djangoapps.grades.tests.utils import answer_problem -from lms.djangoapps.ccx.tests.factories import CcxFactory -from lms.djangoapps.course_blocks.api import get_course_blocks from lms.djangoapps.instructor.enrollment import ( EmailEnrollmentState, enroll_email, @@ -38,9 +39,6 @@ from lms.djangoapps.teams.models import CourseTeamMembership from lms.djangoapps.teams.tests.factories import CourseTeamFactory from openedx.core.djangoapps.ace_common.tests.mixins import EmailTemplateTagMixin from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, get_mock_request -from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentAllowed, anonymous_id_for_user -from common.djangoapps.student.roles import CourseCcxCoachRole -from common.djangoapps.student.tests.factories import AdminFactory, UserFactory from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory @@ -48,7 +46,7 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory class TestSettableEnrollmentState(CacheIsolationTestCase): """ Test the basis class for enrollment tests. """ def setUp(self): - super(TestSettableEnrollmentState, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course_key = CourseLocator('Robot', 'fAKE', 'C--se--ID') def test_mes_create(self): @@ -67,7 +65,7 @@ class TestSettableEnrollmentState(CacheIsolationTestCase): assert mes == ees -class TestEnrollmentChangeBase(six.with_metaclass(ABCMeta, CacheIsolationTestCase)): +class TestEnrollmentChangeBase(CacheIsolationTestCase, metaclass=ABCMeta): """ Test instructor enrollment administration against database effects. @@ -77,7 +75,7 @@ class TestEnrollmentChangeBase(six.with_metaclass(ABCMeta, CacheIsolationTestCas """ def setUp(self): - super(TestEnrollmentChangeBase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course_key = CourseLocator('Robot', 'fAKE', 'C--se--ID') def _run_state_change_test(self, before_ideal, after_ideal, action): @@ -372,7 +370,7 @@ class TestInstructorEnrollmentStudentModule(SharedModuleStoreTestCase): """ Test student module manipulations. """ @classmethod def setUpClass(cls): - super(TestInstructorEnrollmentStudentModule, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory( name='fake', org='course', @@ -403,7 +401,7 @@ class TestInstructorEnrollmentStudentModule(SharedModuleStoreTestCase): ) def setUp(self): - super(TestInstructorEnrollmentStudentModule, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory() @@ -482,8 +480,8 @@ class TestInstructorEnrollmentStudentModule(SharedModuleStoreTestCase): # Create a submission and score for the student using the submissions API student_item = { 'student_id': anonymous_id_for_user(user, self.course_key), - 'course_id': text_type(self.course_key), - 'item_id': text_type(problem_location), + 'course_id': str(self.course_key), + 'item_id': str(problem_location), 'item_type': 'openassessment' } submission = sub_api.create_submission(student_item, 'test answer') @@ -708,7 +706,7 @@ class TestStudentModuleGrading(SharedModuleStoreTestCase): """ @classmethod def setUpClass(cls): - super(TestStudentModuleGrading, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() cls.chapter = ItemFactory.create( parent=cls.course, @@ -743,7 +741,7 @@ class TestStudentModuleGrading(SharedModuleStoreTestCase): @classmethod def tearDownClass(cls): - super(TestStudentModuleGrading, cls).tearDownClass() + super().tearDownClass() set_current_request(None) def _get_subsection_grade_and_verify(self, all_earned, all_possible, graded_earned, graded_possible): @@ -780,7 +778,7 @@ class TestStudentModuleGrading(SharedModuleStoreTestCase): self._get_subsection_grade_and_verify(0, 1, 0, 1) -class EnrollmentObjects(object): +class EnrollmentObjects: """ Container for enrollment objects. @@ -856,13 +854,13 @@ class TestSendBetaRoleEmail(CacheIsolationTestCase): """ def setUp(self): - super(TestSendBetaRoleEmail, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create() self.email_params = {'course': 'Robot Super Course'} def test_bad_action(self): bad_action = 'beta_tester' - error_msg = u"Unexpected action received '{}' - expected 'add' or 'remove'".format(bad_action) + error_msg = f"Unexpected action received '{bad_action}' - expected 'add' or 'remove'" with self.assertRaisesRegex(ValueError, error_msg): send_beta_role_email(bad_action, self.user, self.email_params) @@ -876,12 +874,12 @@ class TestGetEmailParamsCCX(SharedModuleStoreTestCase): @classmethod def setUpClass(cls): - super(TestGetEmailParamsCCX, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() @patch.dict('django.conf.settings.FEATURES', {'CUSTOM_COURSES_EDX': True}) def setUp(self): - super(TestGetEmailParamsCCX, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.coach = AdminFactory.create() role = CourseCcxCoachRole(self.course.id) role.add_users(self.coach) @@ -890,12 +888,12 @@ class TestGetEmailParamsCCX(SharedModuleStoreTestCase): # Explicitly construct what we expect the course URLs to be site = settings.SITE_NAME - self.course_url = u'https://{}/courses/{}/'.format( + self.course_url = 'https://{}/courses/{}/'.format( site, self.course_key ) self.course_about_url = self.course_url + 'about' - self.registration_url = u'https://{}/register'.format(site) + self.registration_url = f'https://{site}/register' @patch.dict('django.conf.settings.FEATURES', {'CUSTOM_COURSES_EDX': True}) def test_ccx_enrollment_email_params(self): @@ -922,17 +920,17 @@ class TestGetEmailParams(SharedModuleStoreTestCase): """ @classmethod def setUpClass(cls): - super(TestGetEmailParams, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() # Explicitly construct what we expect the course URLs to be site = settings.SITE_NAME - cls.course_url = u'https://{}/courses/{}/'.format( + cls.course_url = 'https://{}/courses/{}/'.format( site, - text_type(cls.course.id) + str(cls.course.id) ) cls.course_about_url = cls.course_url + 'about' - cls.registration_url = u'https://{}/register'.format(site) + cls.registration_url = f'https://{site}/register' def test_normal_params(self): # For a normal site, what do we expect to get for the URLs? @@ -967,14 +965,14 @@ class TestRenderMessageToString(EmailTemplateTagMixin, SharedModuleStoreTestCase @classmethod def setUpClass(cls): - super(TestRenderMessageToString, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() cls.subject_template = 'instructor/edx_ace/allowedenroll/email/subject.txt' cls.message_template = 'instructor/edx_ace/allowedenroll/email/body.txt' @patch.dict('django.conf.settings.FEATURES', {'CUSTOM_COURSES_EDX': True}) def setUp(self): - super(TestRenderMessageToString, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() coach = AdminFactory.create() role = CourseCcxCoachRole(self.course.id) role.add_users(coach) @@ -1034,7 +1032,7 @@ class TestRenderMessageToString(EmailTemplateTagMixin, SharedModuleStoreTestCase subject, message = self.get_subject_and_message('eo') language_after_rendering = get_language() - you_have_been_invited_in_esperanto = u"Ýöü hävé ßéén" + you_have_been_invited_in_esperanto = "Ýöü hävé ßéén" assert you_have_been_invited_in_esperanto in subject assert you_have_been_invited_in_esperanto in message assert settings.LANGUAGE_CODE == language_after_rendering @@ -1062,7 +1060,7 @@ class TestRenderMessageToString(EmailTemplateTagMixin, SharedModuleStoreTestCase assert self.ccx.display_name in subject assert self.ccx.display_name in message site = settings.SITE_NAME - course_url = u'https://{}/courses/{}/'.format( + course_url = 'https://{}/courses/{}/'.format( site, self.course_key ) @@ -1100,7 +1098,7 @@ class TestRenderMessageToString(EmailTemplateTagMixin, SharedModuleStoreTestCase assert self.ccx.display_name in subject assert self.ccx.display_name in message site = settings.SITE_NAME - registration_url = u'https://{}/register'.format(site) + registration_url = f'https://{site}/register' assert registration_url in message @patch.dict('django.conf.settings.FEATURES', {'CUSTOM_COURSES_EDX': True}) diff --git a/lms/djangoapps/instructor/tests/test_proctoring.py b/lms/djangoapps/instructor/tests/test_proctoring.py index 79e1a756ed..95f25f72f6 100644 --- a/lms/djangoapps/instructor/tests/test_proctoring.py +++ b/lms/djangoapps/instructor/tests/test_proctoring.py @@ -2,16 +2,16 @@ Unit tests for Edx Proctoring feature flag in new instructor dashboard. """ +from unittest.mock import patch + import ddt from django.apps import apps from django.conf import settings from django.urls import reverse -from mock import patch -from six import text_type - from edx_proctoring.api import create_exam from edx_proctoring.backends.tests.test_backend import TestBackendProvider from edx_toggles.toggles.testutils import override_waffle_flag + from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole from common.djangoapps.student.tests.factories import AdminFactory from lms.djangoapps.courseware.toggles import EXAM_RESUME_PROCTORING_IMPROVEMENTS @@ -28,12 +28,12 @@ class TestProctoringDashboardViews(SharedModuleStoreTestCase): @classmethod def setUpClass(cls): - super(TestProctoringDashboardViews, cls).setUpClass() + super().setUpClass() button = '' # lint-amnesty, pylint: disable=line-too-long cls.proctoring_link = button def setUp(self): - super(TestProctoringDashboardViews, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # Create instructor account self.instructor = AdminFactory.create() @@ -43,7 +43,7 @@ class TestProctoringDashboardViews(SharedModuleStoreTestCase): """ Create URL for instructor dashboard """ - self.url = reverse('instructor_dashboard', kwargs={'course_id': text_type(course.id)}) # lint-amnesty, pylint: disable=attribute-defined-outside-init + self.url = reverse('instructor_dashboard', kwargs={'course_id': str(course.id)}) # lint-amnesty, pylint: disable=attribute-defined-outside-init def setup_course(self, enable_proctored_exams, enable_timed_exams): """ diff --git a/lms/djangoapps/instructor/tests/test_services.py b/lms/djangoapps/instructor/tests/test_services.py index b7985d6ff1..e8ea894900 100644 --- a/lms/djangoapps/instructor/tests/test_services.py +++ b/lms/djangoapps/instructor/tests/test_services.py @@ -1,22 +1,21 @@ """ Tests for the InstructorService """ - - import json +from unittest import mock + import pytest import mock import six +from django.core.exceptions import ObjectDoesNotExist from opaque_keys import InvalidKeyError -from django.core.exceptions import ObjectDoesNotExist - +from common.djangoapps.student.models import CourseEnrollment +from common.djangoapps.student.tests.factories import UserFactory from lms.djangoapps.courseware.models import StudentModule from lms.djangoapps.instructor.access import allow_access from lms.djangoapps.instructor.services import InstructorService from lms.djangoapps.instructor.tests.test_tools import msk_from_problem_urlname -from common.djangoapps.student.models import CourseEnrollment -from common.djangoapps.student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -28,7 +27,7 @@ class InstructorServiceTests(SharedModuleStoreTestCase): @classmethod def setUpClass(cls): - super(InstructorServiceTests, cls).setUpClass() + super().setUpClass() cls.email = 'escalation@test.com' cls.course = CourseFactory.create(proctoring_escalation_email=cls.email) cls.problem_location = msk_from_problem_urlname( @@ -39,11 +38,11 @@ class InstructorServiceTests(SharedModuleStoreTestCase): cls.course.id, 'robot-some-other_problem-urlname' ) - cls.problem_urlname = six.text_type(cls.problem_location) - cls.other_problem_urlname = six.text_type(cls.other_problem_location) + cls.problem_urlname = str(cls.problem_location) + cls.other_problem_urlname = str(cls.other_problem_location) def setUp(self): - super(InstructorServiceTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.student = UserFactory() CourseEnrollment.enroll(self.student, self.course.id) @@ -68,7 +67,7 @@ class InstructorServiceTests(SharedModuleStoreTestCase): self.service.delete_student_attempt( self.student.username, - six.text_type(self.course.id), + str(self.course.id), self.problem_urlname, requesting_user=self.student, ) @@ -84,7 +83,7 @@ class InstructorServiceTests(SharedModuleStoreTestCase): result = self.service.delete_student_attempt( # lint-amnesty, pylint: disable=assignment-from-none self.student.username, - six.text_type(self.course.id), + str(self.course.id), 'foo/bar/baz', requesting_user=self.student, ) @@ -97,7 +96,7 @@ class InstructorServiceTests(SharedModuleStoreTestCase): result = self.service.delete_student_attempt( # lint-amnesty, pylint: disable=assignment-from-none 'bad_student', - six.text_type(self.course.id), + str(self.course.id), 'foo/bar/baz', requesting_user=self.student, ) @@ -110,7 +109,7 @@ class InstructorServiceTests(SharedModuleStoreTestCase): result = self.service.delete_student_attempt( # lint-amnesty, pylint: disable=assignment-from-none self.student.username, - six.text_type(self.course.id), + str(self.course.id), self.other_problem_urlname, requesting_user=self.student, ) @@ -122,7 +121,7 @@ class InstructorServiceTests(SharedModuleStoreTestCase): """ result = self.service.is_course_staff( self.student, - six.text_type(self.course.id) + str(self.course.id) ) assert not result @@ -130,7 +129,7 @@ class InstructorServiceTests(SharedModuleStoreTestCase): allow_access(self.course, self.student, 'staff') result = self.service.is_course_staff( self.student, - six.text_type(self.course.id) + str(self.course.id) ) assert result @@ -140,11 +139,11 @@ class InstructorServiceTests(SharedModuleStoreTestCase): """ requester_name = "edx-proctoring" email = "edx-proctoring@edx.org" - subject = u"Proctored Exam Review: {review_status}".format(review_status="Suspicious") + subject = "Proctored Exam Review: {review_status}".format(review_status="Suspicious") - body = u"A proctored exam attempt for {exam_name} in {course_name} by username: {student_username} was " \ - u"reviewed as {review_status} by the proctored exam review provider.\n" \ - u"Review link: {url}" + body = "A proctored exam attempt for {exam_name} in {course_name} by username: {student_username} was " \ + "reviewed as {review_status} by the proctored exam review provider.\n" \ + "Review link: {url}" args = { 'exam_name': 'test_exam', 'student_username': 'test_student', @@ -157,7 +156,7 @@ class InstructorServiceTests(SharedModuleStoreTestCase): with mock.patch("lms.djangoapps.instructor.services.create_zendesk_ticket") as mock_create_zendesk_ticket: self.service.send_support_notification( - course_id=six.text_type(self.course.id), + course_id=str(self.course.id), exam_name=args['exam_name'], student_username=args["student_username"], review_status="Suspicious", @@ -169,7 +168,7 @@ class InstructorServiceTests(SharedModuleStoreTestCase): args['url'] = 'http://review/url' with mock.patch("lms.djangoapps.instructor.services.create_zendesk_ticket") as mock_create_zendesk_ticket: self.service.send_support_notification( - course_id=six.text_type(self.course.id), + course_id=str(self.course.id), exam_name=args['exam_name'], student_username=args["student_username"], review_status="Suspicious", diff --git a/lms/djangoapps/instructor/tests/test_spoc_gradebook.py b/lms/djangoapps/instructor/tests/test_spoc_gradebook.py index 36f5e3ac8f..42be8e9210 100644 --- a/lms/djangoapps/instructor/tests/test_spoc_gradebook.py +++ b/lms/djangoapps/instructor/tests/test_spoc_gradebook.py @@ -1,16 +1,12 @@ """ Tests of the instructor dashboard spoc gradebook """ - - from django.urls import reverse -from six import text_type -from six.moves import range from capa.tests.response_xml_factory import StringResponseXMLFactory +from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory from lms.djangoapps.courseware.tests.factories import StudentModuleFactory from lms.djangoapps.grades.api import task_compute_all_grades_for_course -from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory @@ -27,7 +23,7 @@ class TestGradebook(SharedModuleStoreTestCase): @classmethod def setUpClass(cls): - super(TestGradebook, cls).setUpClass() + super().setUpClass() # Create a course with the desired grading policy (from our class attribute) kwargs = {} @@ -57,7 +53,7 @@ class TestGradebook(SharedModuleStoreTestCase): ] def setUp(self): - super(TestGradebook, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() instructor = AdminFactory.create() self.client.login(username=instructor.username, password='test') @@ -75,11 +71,11 @@ class TestGradebook(SharedModuleStoreTestCase): course_id=self.course.id, module_state_key=item.location ) - task_compute_all_grades_for_course.apply_async(kwargs={'course_key': text_type(self.course.id)}) + task_compute_all_grades_for_course.apply_async(kwargs={'course_key': str(self.course.id)}) self.response = self.client.get(reverse( 'spoc_gradebook', - args=(text_type(self.course.id),) + args=(str(self.course.id),) )) assert self.response.status_code == 200 @@ -92,7 +88,7 @@ class TestDefaultGradingPolicy(TestGradebook): """ def test_all_users_listed(self): for user in self.users: - assert user.username in text_type(self.response.content, 'utf-8') + assert user.username in str(self.response.content, 'utf-8') def test_default_policy(self): # Default >= 50% passes, so Users 5-10 should be passing for Homework 1 [6] @@ -135,10 +131,10 @@ class TestLetterCutoffPolicy(TestGradebook): def test_styles(self): - self.assertContains(self.response, u"grade_A {color:green;}") - self.assertContains(self.response, u"grade_B {color:Chocolate;}") - self.assertContains(self.response, u"grade_C {color:DarkSlateGray;}") - self.assertContains(self.response, u"grade_D {color:DarkSlateGray;}") + self.assertContains(self.response, "grade_A {color:green;}") + self.assertContains(self.response, "grade_B {color:Chocolate;}") + self.assertContains(self.response, "grade_C {color:DarkSlateGray;}") + self.assertContains(self.response, "grade_D {color:DarkSlateGray;}") def test_assigned_grades(self): # Users 9-10 have >= 90% on Homeworks [2] diff --git a/lms/djangoapps/instructor/tests/test_tools.py b/lms/djangoapps/instructor/tests/test_tools.py index c6a02a921e..0ad231bf21 100644 --- a/lms/djangoapps/instructor/tests/test_tools.py +++ b/lms/djangoapps/instructor/tests/test_tools.py @@ -6,20 +6,22 @@ Tests for views/tools.py. import datetime import json import unittest +from unittest import mock + import pytest import mock import six from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.core.exceptions import MultipleObjectsReturned from django.test import TestCase +from edx_when.api import set_dates_for_course +from edx_when.field_data import DateLookupFieldData from opaque_keys.edx.keys import CourseKey from pytz import UTC -from edx_when.api import set_dates_for_course -from edx_when.field_data import DateLookupFieldData +from common.djangoapps.student.tests.factories import UserFactory from openedx.core.djangoapps.course_date_signals import handlers from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory -from common.djangoapps.student.tests.factories import UserFactory from xmodule.fields import Date from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory @@ -34,7 +36,7 @@ class TestDashboardError(unittest.TestCase): Test DashboardError exceptions. """ def test_response(self): - error = tools.DashboardError(u'Oh noes!') + error = tools.DashboardError('Oh noes!') response = json.loads(error.response().content.decode('utf-8')) assert response == {'error': 'Oh noes!'} @@ -73,7 +75,7 @@ class TestRequireStudentIdentifier(TestCase): """ Fixtures """ - super(TestRequireStudentIdentifier, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.student = UserFactory.create() def test_valid_student_id(self): @@ -102,7 +104,7 @@ class TestFindUnit(SharedModuleStoreTestCase): """ @classmethod def setUpClass(cls): - super(TestFindUnit, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() with cls.store.bulk_operations(cls.course.id, emit_signals=False): week1 = ItemFactory.create(parent=cls.course) @@ -112,7 +114,7 @@ class TestFindUnit(SharedModuleStoreTestCase): """ Test finding a nested unit. """ - url = six.text_type(self.homework.location) + url = str(self.homework.location) found_unit = tools.find_unit(self.course, url) assert found_unit.location == self.homework.location @@ -157,7 +159,7 @@ class TestGetUnitsWithDueDate(ModuleStoreTestCase): """ URLs for sequence of nodes. """ - return sorted(six.text_type(i.location) for i in seq) + return sorted(str(i.location) for i in seq) assert urls(tools.get_units_with_due_date(self.course)) == urls((self.week1, self.week2)) @@ -176,14 +178,11 @@ class TestTitleOrUrl(unittest.TestCase): """ Mock implementation of __unicode__ or __str__ for the unit's location. """ - return u'test:hello' + return 'test:hello' unit = mock.Mock(display_name=None) - if six.PY2: - unit.location.__unicode__ = mock_location_text - else: - unit.location.__str__ = mock_location_text - assert tools.title_or_url(unit) == u'test:hello' + unit.location.__str__ = mock_location_text + assert tools.title_or_url(unit) == 'test:hello' def inject_field_data(blocks, course, user): @@ -201,7 +200,7 @@ class TestSetDueDateExtension(ModuleStoreTestCase): """ Fixtures. """ - super(TestSetDueDateExtension, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.due = due = datetime.datetime(2010, 5, 12, 2, 42, tzinfo=UTC) course = CourseFactory.create() @@ -290,7 +289,7 @@ class TestDataDumps(ModuleStoreTestCase): """ Fixtures. """ - super(TestDataDumps, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() due = datetime.datetime(2010, 5, 12, 2, 42, tzinfo=UTC) course = CourseFactory.create() @@ -378,7 +377,7 @@ class TestStudentFromIdentifier(TestCase): """ @classmethod def setUpClass(cls): - super(TestStudentFromIdentifier, cls).setUpClass() + super().setUpClass() cls.valid_student = UserFactory.create(username='baz@touchstone') cls.student_conflicting_email = UserFactory.create(email='foo@touchstone.com') cls.student_conflicting_username = UserFactory.create(username='foo@touchstone.com') diff --git a/lms/djangoapps/instructor/tests/utils.py b/lms/djangoapps/instructor/tests/utils.py index 084e619498..6b724365f1 100644 --- a/lms/djangoapps/instructor/tests/utils.py +++ b/lms/djangoapps/instructor/tests/utils.py @@ -7,19 +7,18 @@ import datetime import json import random -import six from pytz import UTC from common.djangoapps.util.date_utils import get_default_time_display -class FakeInfo(object): +class FakeInfo: """Parent class for faking objects used in tests""" FEATURES = [] def __init__(self): for feature in self.FEATURES: - setattr(self, feature, u'expected') + setattr(self, feature, 'expected') def to_dict(self): """ Returns a dict representation of the object """ @@ -35,7 +34,7 @@ class FakeContentTask(FakeInfo): ] def __init__(self, email_id, num_sent, num_failed, sent_to): # lint-amnesty, pylint: disable=unused-argument - super(FakeContentTask, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() self.task_input = {'email_id': email_id} self.task_input = json.dumps(self.task_input) self.task_output = {'succeeded': num_sent, 'failed': num_failed} @@ -57,8 +56,8 @@ class FakeEmail(FakeInfo): ] def __init__(self, email_id): - super(FakeEmail, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments - self.id = six.text_type(email_id) # pylint: disable=invalid-name + super().__init__() + self.id = str(email_id) # pylint: disable=invalid-name # Select a random data for create field year = random.randint(1950, 2000) month = random.randint(1, 12) @@ -69,7 +68,7 @@ class FakeEmail(FakeInfo): self.targets = FakeTargetGroup() -class FakeTarget(object): +class FakeTarget: """ Corresponding fake target for a fake email """ target_type = "expected" @@ -78,7 +77,7 @@ class FakeTarget(object): return self.target_type -class FakeTargetGroup(object): +class FakeTargetGroup: """ Mocks out the M2M relationship between FakeEmail and FakeTarget """ def all(self): """ Mocks out a django method """ @@ -88,21 +87,21 @@ class FakeTargetGroup(object): class FakeEmailInfo(FakeInfo): """ Fake email information object """ FEATURES = [ - u'created', - u'sent_to', - u'email', - u'number_sent', - u'requester', + 'created', + 'sent_to', + 'email', + 'number_sent', + 'requester', ] EMAIL_FEATURES = [ - u'subject', - u'html_message', - u'id' + 'subject', + 'html_message', + 'id' ] def __init__(self, fake_email, num_sent, num_failed): - super(FakeEmailInfo, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() self.created = get_default_time_display(fake_email.created) number_sent = str(num_sent) + ' sent' @@ -112,5 +111,5 @@ class FakeEmailInfo(FakeInfo): self.number_sent = number_sent fake_email_dict = fake_email.to_dict() self.email = {feature: fake_email_dict[feature] for feature in self.EMAIL_FEATURES} - self.requester = u'expected' - self.sent_to = [u'expected'] + self.requester = 'expected' + self.sent_to = ['expected'] diff --git a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py index c13b0acf8a..783f5da8f7 100644 --- a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py +++ b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py @@ -5,23 +5,23 @@ Unit tests for instructor_dashboard.py. import datetime import re +from unittest.mock import patch import ddt -import six from django.conf import settings from django.contrib.sites.models import Site from django.test.utils import override_settings from django.urls import reverse -from mock import patch +from edx_toggles.toggles.testutils import override_waffle_flag from pyquery import PyQuery as pq from pytz import UTC -from six import text_type -from six.moves import range -from common.test.utils import XssTestMixin from common.djangoapps.course_modes.models import CourseMode -from edx_toggles.toggles.testutils import override_waffle_flag # lint-amnesty, pylint: disable=wrong-import-order from common.djangoapps.edxmako.shortcuts import render_to_response +from common.djangoapps.student.models import CourseEnrollment +from common.djangoapps.student.roles import CourseFinanceAdminRole # lint-amnesty, pylint: disable=unused-import +from common.djangoapps.student.tests.factories import AdminFactory, CourseAccessRoleFactory, CourseEnrollmentFactory +from common.test.utils import XssTestMixin from lms.djangoapps.courseware.tabs import get_course_tab_list from lms.djangoapps.courseware.tests.factories import StaffFactory, StudentModuleFactory, UserFactory from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase @@ -29,9 +29,6 @@ from lms.djangoapps.grades.config.waffle import WRITABLE_GRADEBOOK, waffle_flags from lms.djangoapps.instructor.toggles import DATA_DOWNLOAD_V2 from lms.djangoapps.instructor.views.gradebook_api import calculate_page_info from openedx.core.djangoapps.site_configuration.models import SiteConfiguration -from common.djangoapps.student.models import CourseEnrollment -from common.djangoapps.student.roles import CourseFinanceAdminRole # lint-amnesty, pylint: disable=unused-import -from common.djangoapps.student.tests.factories import AdminFactory, CourseAccessRoleFactory, CourseEnrollmentFactory from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls @@ -61,7 +58,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT """ Set up tests """ - super(TestInstructorDashboard, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course = CourseFactory.create( grading_policy={"GRADE_CUTOFFS": {"A": 0.75, "B": 0.63, "C": 0.57, "D": 0.5}}, display_name='' @@ -85,21 +82,21 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT self.client.login(username=self.instructor.username, password="test") # URL for instructor dash - self.url = reverse('instructor_dashboard', kwargs={'course_id': text_type(self.course.id)}) + self.url = reverse('instructor_dashboard', kwargs={'course_id': str(self.course.id)}) def get_dashboard_enrollment_message(self): """ Returns expected dashboard enrollment message with link to Insights. """ - return u'Enrollment data is now available in Example.'.format(text_type(self.course.id)) + return 'Enrollment data is now available in Example.'.format(str(self.course.id)) def get_dashboard_analytics_message(self): """ Returns expected dashboard demographic message with link to Insights. """ - return u'For analytics about your course, go to Example.'.format(text_type(self.course.id)) + return 'For analytics about your course, go to Example.'.format(str(self.course.id)) def test_instructor_tab(self): """ @@ -203,7 +200,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT url = reverse( 'instructor_dashboard', kwargs={ - 'course_id': six.text_type(self.course_info.id) + 'course_id': str(self.course_info.id) } ) @@ -237,7 +234,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT url = reverse( 'instructor_dashboard', kwargs={ - 'course_id': six.text_type(self.course_info.id) + 'course_id': str(self.course_info.id) } ) response = self.client.get(url) @@ -269,7 +266,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT url = reverse( 'instructor_dashboard', kwargs={ - 'course_id': six.text_type(self.course_info.id) + 'course_id': str(self.course_info.id) } ) @@ -286,7 +283,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT url = reverse( 'instructor_dashboard', kwargs={ - 'course_id': six.text_type(self.course_info.id) + 'course_id': str(self.course_info.id) } ) @@ -325,7 +322,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT with override_waffle_flag(waffle_flag, active=True): response = self.client.get(self.url) - expected_gradebook_url = 'http://gradebook.local.edx.org/{}'.format(self.course.id) + expected_gradebook_url = f'http://gradebook.local.edx.org/{self.course.id}' self.assertContains(response, expected_gradebook_url) self.assertContains(response, 'View Gradebook') @@ -347,7 +344,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT with override_waffle_flag(waffle_flag, active=True): response = self.client.get(self.url) - expected_gradebook_url = '{}/{}'.format(settings.WRITABLE_GRADEBOOK_URL, self.course.id) + expected_gradebook_url = f'{settings.WRITABLE_GRADEBOOK_URL}/{self.course.id}' self.assertContains(response, expected_gradebook_url) self.assertContains(response, 'View Gradebook') @@ -581,7 +578,7 @@ class TestInstructorDashboardPerformance(ModuleStoreTestCase, LoginEnrollmentTes """ Set up tests """ - super(TestInstructorDashboardPerformance, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course = CourseFactory.create( grading_policy={"GRADE_CUTOFFS": {"A": 0.75, "B": 0.63, "C": 0.57, "D": 0.5}}, display_name='', @@ -643,7 +640,7 @@ class TestInstructorDashboardPerformance(ModuleStoreTestCase, LoginEnrollmentTes problem = ItemFactory.create( category="problem", parent=vertical, - display_name=u"A Problem Block %d" % i, + display_name="A Problem Block %d" % i, weight=1, publish_item=False, metadata={'rerandomize': 'always'}, diff --git a/lms/djangoapps/instructor/toggles.py b/lms/djangoapps/instructor/toggles.py index 4fc8642e9a..042ba5424e 100644 --- a/lms/djangoapps/instructor/toggles.py +++ b/lms/djangoapps/instructor/toggles.py @@ -3,6 +3,7 @@ Waffle flags for instructor dashboard. """ from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace + from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag # lint-amnesty, pylint: disable=unused-import WAFFLE_NAMESPACE = 'instructor' diff --git a/lms/djangoapps/instructor/utils.py b/lms/djangoapps/instructor/utils.py index 36535536f1..adc38bf1ac 100644 --- a/lms/djangoapps/instructor/utils.py +++ b/lms/djangoapps/instructor/utils.py @@ -8,7 +8,7 @@ from lms.djangoapps.courseware.module_render import get_module from xmodule.modulestore.django import modulestore -class DummyRequest(object): +class DummyRequest: """Dummy request""" META = {} diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index ffa0ce20c4..88cb6c9310 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -13,8 +13,6 @@ import random import re import string -import six -import unicodecsv from django.conf import settings from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist, PermissionDenied, ValidationError @@ -36,19 +34,43 @@ from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey from ratelimit.decorators import ratelimit from rest_framework import status -from rest_framework.permissions import IsAuthenticated, IsAdminUser +from rest_framework.permissions import IsAdminUser, IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView -from six import text_type -from six.moves import map, range from submissions import api as sub_api # installed from the edx-submissions repository -from lms.djangoapps.instructor_analytics import basic as instructor_analytics_basic -from lms.djangoapps.instructor_analytics import csvs as instructor_analytics_csvs -from lms.djangoapps.instructor_analytics import distributions as instructor_analytics_distributions # lint-amnesty, pylint: disable=unused-import +from common.djangoapps.course_modes.models import CourseMode +from common.djangoapps.student import auth +from common.djangoapps.student.models import ( + ALLOWEDTOENROLL_TO_ENROLLED, + ALLOWEDTOENROLL_TO_UNENROLLED, + DEFAULT_TRANSITION_STATE, + ENROLLED_TO_ENROLLED, + ENROLLED_TO_UNENROLLED, + UNENROLLED_TO_ALLOWEDTOENROLL, + UNENROLLED_TO_ENROLLED, + UNENROLLED_TO_UNENROLLED, + CourseEnrollment, + CourseEnrollmentAllowed, + EntranceExamConfiguration, + ManualEnrollmentAudit, + Registration, + UserProfile, + anonymous_id_for_user, + get_user_by_username_or_email, + is_email_retired, + unique_id_for_user +) +from common.djangoapps.student.roles import CourseFinanceAdminRole, CourseSalesAdminRole +from common.djangoapps.util.file import ( + FileValidationException, + course_and_time_based_filename_generator, + store_uploaded_file +) +from common.djangoapps.util.json_request import JsonResponse, JsonResponseBadRequest +from common.djangoapps.util.views import require_global_staff from lms.djangoapps.bulk_email.api import is_bulk_email_feature_enabled from lms.djangoapps.bulk_email.models import CourseEmail -from common.djangoapps.course_modes.models import CourseMode from lms.djangoapps.certificates import api as certs_api from lms.djangoapps.certificates.models import ( CertificateInvalidation, @@ -77,6 +99,9 @@ from lms.djangoapps.instructor.enrollment import ( ) from lms.djangoapps.instructor.views import INVOICE_KEY from lms.djangoapps.instructor.views.instructor_task_helpers import extract_email_features, extract_task_features +from lms.djangoapps.instructor_analytics import basic as instructor_analytics_basic +from lms.djangoapps.instructor_analytics import csvs as instructor_analytics_csvs +from lms.djangoapps.instructor_analytics import distributions as instructor_analytics_distributions # lint-amnesty, pylint: disable=unused-import from lms.djangoapps.instructor_task import api as task_api from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError, QueueConnectionError from lms.djangoapps.instructor_task.models import ReportStore @@ -94,40 +119,9 @@ from openedx.core.djangoapps.user_api.preferences.api import get_user_preference from openedx.core.djangolib.markup import HTML, Text from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin -from common.djangoapps.student import auth -from common.djangoapps.student.models import ( - ALLOWEDTOENROLL_TO_ENROLLED, - ALLOWEDTOENROLL_TO_UNENROLLED, - DEFAULT_TRANSITION_STATE, - ENROLLED_TO_ENROLLED, - ENROLLED_TO_UNENROLLED, - UNENROLLED_TO_ALLOWEDTOENROLL, - UNENROLLED_TO_ENROLLED, - UNENROLLED_TO_UNENROLLED, - CourseEnrollment, - CourseEnrollmentAllowed, - EntranceExamConfiguration, - ManualEnrollmentAudit, - Registration, - UserProfile, - anonymous_id_for_user, - get_user_by_username_or_email, - is_email_retired, - unique_id_for_user -) -from common.djangoapps.student.roles import CourseFinanceAdminRole, CourseSalesAdminRole -from common.djangoapps.util.file import ( - FileValidationException, - UniversalNewlineIterator, - course_and_time_based_filename_generator, - store_uploaded_file -) -from common.djangoapps.util.json_request import JsonResponse, JsonResponseBadRequest -from common.djangoapps.util.views import require_global_staff from xmodule.modulestore.django import modulestore from .. import permissions - from .tools import ( dump_module_extensions, dump_student_extensions, @@ -144,7 +138,7 @@ log = logging.getLogger(__name__) TASK_SUBMISSION_OK = 'created' -SUCCESS_MESSAGE_TEMPLATE = _(u"The {report_type} report is being created. " +SUCCESS_MESSAGE_TEMPLATE = _("The {report_type} report is being created. " "To view the status of the report, see Pending Tasks below.") @@ -164,7 +158,7 @@ def common_exceptions_400(func): except MultipleObjectsReturned: message = _('Found a conflict with given identifier. Please try an alternative identifier') except (AlreadyRunningError, QueueConnectionError, AttributeError) as err: - message = six.text_type(err) + message = str(err) if use_json: return JsonResponseBadRequest(message) @@ -247,7 +241,7 @@ def require_sales_admin(func): try: course_key = CourseKey.from_string(course_id) except InvalidKeyError: - log.error(u"Unable to find course with course key %s", course_id) + log.error("Unable to find course with course key %s", course_id) return HttpResponseNotFound() access = auth.user_has_role(request.user, CourseSalesAdminRole(course_key)) @@ -272,7 +266,7 @@ def require_finance_admin(func): try: course_key = CourseKey.from_string(course_id) except InvalidKeyError: - log.error(u"Unable to find course with course key %s", course_id) + log.error("Unable to find course with course key %s", course_id) return HttpResponseNotFound() access = auth.user_has_role(request.user, CourseFinanceAdminRole(course_key)) @@ -362,7 +356,7 @@ def register_and_enroll_students(request, course_id): # pylint: disable=too-man # verify that we have exactly four columns in every row but allow for blank lines if len(student) != 4: if student: - error = _(u'Data in row #{row_num} must have exactly four columns: ' + error = _('Data in row #{row_num} must have exactly four columns: ' 'email, username, full name, and country').format(row_num=row_num) general_errors.append({ 'username': '', @@ -384,7 +378,7 @@ def register_and_enroll_students(request, course_id): # pylint: disable=too-man row_errors.append({ 'username': username, 'email': email, - 'response': _(u'Invalid email {email_address}.').format(email_address=email) + 'response': _('Invalid email {email_address}.').format(email_address=email) }) else: if User.objects.filter(email=email).exists(): @@ -396,17 +390,17 @@ def register_and_enroll_students(request, course_id): # pylint: disable=too-man # if it's not an exact match then just display a warning message, but continue onwards if not User.objects.filter(email=email, username=username).exists(): warning_message = _( - u'An account with email {email} exists but the provided username {username} ' - u'is different. Enrolling anyway with {email}.' + 'An account with email {email} exists but the provided username {username} ' + 'is different. Enrolling anyway with {email}.' ).format(email=email, username=username) warnings.append({ 'username': username, 'email': email, 'response': warning_message }) - log.warning(u'email %s already exist', email) + log.warning('email %s already exist', email) else: log.info( - u"user already exists with username '%s' and email '%s'", + "user already exists with username '%s' and email '%s'", username, email ) @@ -433,10 +427,10 @@ def register_and_enroll_students(request, course_id): # pylint: disable=too-man row_errors.append({ 'username': username, 'email': email, - 'response': _(u'Invalid email {email_address}.').format(email_address=email), + 'response': _('Invalid email {email_address}.').format(email_address=email), }) - log.warning(u'Email address %s is associated with a retired user, so course enrollment was ' + # lint-amnesty, pylint: disable=logging-not-lazy - u'blocked.', email) + log.warning('Email address %s is associated with a retired user, so course enrollment was ' + # lint-amnesty, pylint: disable=logging-not-lazy + 'blocked.', email) else: # This email does not yet exist, so we need to create a new account # If username already exists in the database, then create_and_enroll_user @@ -468,7 +462,7 @@ def generate_random_string(length): char for char in string.ascii_uppercase + string.digits + string.ascii_lowercase if char not in 'aAeEiIoOuU1l' ] - return ''.join((random.choice(chars) for i in range(length))) + return ''.join(random.choice(chars) for i in range(length)) def generate_unique_password(generated_passwords, password_length=12): @@ -527,7 +521,7 @@ def create_manual_course_enrollment(user, course_id, mode, enrolled_by, reason, enrolled_by, user.email, state_transition, reason, enrollment_obj ) - log.info(u'user %s enrolled in the course %s', user.username, course_id) + log.info('user %s enrolled in the course %s', user.username, course_id) return enrollment_obj @@ -571,7 +565,7 @@ def create_and_enroll_user(email, username, name, country, password, course_id, errors.append({ 'username': username, 'email': email, - 'response': _(u'Username {user} already exists.').format(user=username) + 'response': _('Username {user} already exists.').format(user=username) }) except Exception as ex: # pylint: disable=broad-except log.exception(type(ex).__name__) @@ -590,18 +584,18 @@ def create_and_enroll_user(email, username, name, country, password, course_id, send_mail_to_student(email, email_params) except Exception as ex: # pylint: disable=broad-except log.exception( - u"Exception '{exception}' raised while sending email to new user.".format(exception=type(ex).__name__) + "Exception '{exception}' raised while sending email to new user.".format(exception=type(ex).__name__) ) errors.append({ 'username': username, 'email': email, 'response': - _(u"Error '{error}' while sending email to new user (user email={email}). " - u"Without the email student would not be able to login. " - u"Please contact support for further information.").format(error=type(ex).__name__, email=email), + _("Error '{error}' while sending email to new user (user email={email}). " + "Without the email student would not be able to login. " + "Please contact support for further information.").format(error=type(ex).__name__, email=email), }) else: - log.info(u'email sent to new created user at %s', email) + log.info('email sent to new created user at %s', email) return errors @@ -735,7 +729,7 @@ def students_update_enrollment(request, course_id): # lint-amnesty, pylint: dis else: return HttpResponseBadRequest(strip_tags( - u"Unrecognized action '{}'".format(action) + f"Unrecognized action '{action}'" )) except ValidationError: @@ -749,7 +743,7 @@ def students_update_enrollment(request, course_id): # lint-amnesty, pylint: dis except Exception as exc: # pylint: disable=broad-except # catch and log any exceptions # so that one error doesn't cause a 500. - log.exception(u"Error while #{}ing student") + log.exception("Error while #{}ing student") log.exception(exc) results.append({ 'identifier': identifier, @@ -820,7 +814,7 @@ def bulk_beta_modify_access(request, course_id): revoke_access(course, user, rolename) else: return HttpResponseBadRequest(strip_tags( - u"Unrecognized action '{}'".format(action) + f"Unrecognized action '{action}'" )) except User.DoesNotExist: error = True @@ -829,7 +823,7 @@ def bulk_beta_modify_access(request, course_id): # catch and log any unexpected exceptions # so that one error doesn't cause a 500. except Exception as exc: # pylint: disable=broad-except - log.exception(u"Error while #{}ing student") + log.exception("Error while #{}ing student") log.exception(exc) error = True else: @@ -907,7 +901,7 @@ def modify_access(request, course_id): action = request.POST.get('action') if rolename not in ROLES: - error = strip_tags(u"unknown rolename '{}'".format(rolename)) + error = strip_tags(f"unknown rolename '{rolename}'") log.error(error) return HttpResponseBadRequest(error) @@ -927,7 +921,7 @@ def modify_access(request, course_id): revoke_access(course, user, rolename) else: return HttpResponseBadRequest(strip_tags( - u"unrecognized action u'{}'".format(action) + f"unrecognized action u'{action}'" )) response_payload = { @@ -984,7 +978,7 @@ def list_course_role_members(request, course_id): } response_payload = { - 'course_id': text_type(course_id), + 'course_id': str(course_id), rolename: list(map(extract_user_info, list_with_level( course, rolename ))), @@ -1082,7 +1076,7 @@ def get_grading_config(request, course_id): grading_config_summary = instructor_analytics_basic.dump_grading_context(course) response_payload = { - 'course_id': text_type(course_id), + 'course_id': str(course_id), 'grading_config_summary': grading_config_summary, } return JsonResponse(response_payload) @@ -1205,7 +1199,7 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red if not csv: student_data = instructor_analytics_basic.enrolled_students_features(course_key, query_features) response_payload = { - 'course_id': six.text_type(course_key), + 'course_id': str(course_key), 'students': student_data, 'students_count': len(student_data), 'queried_features': query_features, @@ -1254,10 +1248,7 @@ def _cohorts_csv_validator(file_storage, file_to_validate): Verifies that the expected columns are present in the CSV used to add users to cohorts. """ with file_storage.open(file_to_validate) as f: - if six.PY2: - reader = unicodecsv.reader(UniversalNewlineIterator(f), encoding='utf-8') - else: - reader = csv.reader(f.read().decode('utf-8').splitlines()) + reader = csv.reader(f.read().decode('utf-8').splitlines()) try: fieldnames = next(reader) @@ -1296,7 +1287,7 @@ def add_users_to_cohorts(request, course_id): # The task will assume the default file storage. task_api.submit_cohort_students(request, course_key, filename) except (FileValidationException, PermissionDenied) as err: - return JsonResponse({"error": six.text_type(err)}, status=400) + return JsonResponse({"error": str(err)}, status=400) return JsonResponse() @@ -1396,16 +1387,14 @@ def get_anon_ids(request, course_id): def csv_response(filename, header, rows): """Returns a CSV http response for the given header and rows (excel/utf-8).""" response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = u'attachment; filename={0}'.format( - text_type(filename).encode('utf-8') if six.PY2 else text_type(filename) - ) + response['Content-Disposition'] = 'attachment; filename={}'.format(str(filename)) writer = csv.writer(response, dialect='excel', quotechar='"', quoting=csv.QUOTE_ALL) # In practice, there should not be non-ascii data in this query, # but trying to do the right thing anyway. - encoded = [text_type(s) for s in header] + encoded = [str(s) for s in header] writer.writerow(encoded) for row in rows: - encoded = [text_type(s) for s in row] + encoded = [str(s) for s in row] writer.writerow(encoded) return response @@ -1415,7 +1404,7 @@ def get_anon_ids(request, course_id): header = ['User ID', 'Anonymized User ID', 'Course Specific Anonymized User ID'] rows = [[s.id, unique_id_for_user(s), anonymous_id_for_user(s, course_id)] for s in students] - return csv_response(text_type(course_id).replace('/', '-') + '-anon-ids.csv', header, rows) + return csv_response(str(course_id).replace('/', '-') + '-anon-ids.csv', header, rows) @require_POST @@ -1450,23 +1439,23 @@ def get_student_enrollment_status(request, course_id): # records, so let the lack of a User slide. pass - enrollment_status = _(u'Enrollment status for {student}: unknown').format(student=unique_student_identifier) + enrollment_status = _('Enrollment status for {student}: unknown').format(student=unique_student_identifier) if user and mode: if is_active: - enrollment_status = _(u'Enrollment status for {student}: active').format(student=user) + enrollment_status = _('Enrollment status for {student}: active').format(student=user) else: - enrollment_status = _(u'Enrollment status for {student}: inactive').format(student=user) + enrollment_status = _('Enrollment status for {student}: inactive').format(student=user) else: email = user.email if user else unique_student_identifier allowed = CourseEnrollmentAllowed.may_enroll_and_unenrolled(course_id) if allowed and email in [cea.email for cea in allowed]: - enrollment_status = _(u'Enrollment status for {student}: pending').format(student=email) + enrollment_status = _('Enrollment status for {student}: pending').format(student=email) else: - enrollment_status = _(u'Enrollment status for {student}: never enrolled').format(student=email) + enrollment_status = _('Enrollment status for {student}: never enrolled').format(student=email) response_payload = { - 'course_id': text_type(course_id), + 'course_id': str(course_id), 'error': error, 'enrollment_status': enrollment_status } @@ -1496,10 +1485,10 @@ def get_student_progress_url(request, course_id): course_id = CourseKey.from_string(course_id) user = get_student_from_identifier(request.POST.get('unique_student_identifier')) - progress_url = reverse('student_progress', kwargs={'course_id': text_type(course_id), 'student_id': user.id}) + progress_url = reverse('student_progress', kwargs={'course_id': str(course_id), 'student_id': user.id}) response_payload = { - 'course_id': text_type(course_id), + 'course_id': str(course_id), 'progress_url': progress_url, } return JsonResponse(response_payload) @@ -1725,7 +1714,7 @@ def rescore_problem(request, course_id): only_if_higher, ) except NotImplementedError as exc: - return HttpResponseBadRequest(text_type(exc)) + return HttpResponseBadRequest(str(exc)) elif all_students: try: @@ -1735,7 +1724,7 @@ def rescore_problem(request, course_id): only_if_higher, ) except NotImplementedError as exc: - return HttpResponseBadRequest(text_type(exc)) + return HttpResponseBadRequest(str(exc)) else: return HttpResponseBadRequest() @@ -1765,16 +1754,16 @@ def override_problem_score(request, course_id): # lint-amnesty, pylint: disable if student_identifier is not None: student = get_student_from_identifier(student_identifier) else: - return _create_error_response(request, u"Invalid student ID {}.".format(student_identifier)) + return _create_error_response(request, f"Invalid student ID {student_identifier}.") try: usage_key = UsageKey.from_string(problem_to_reset).map_into_course(course_key) except InvalidKeyError: - return _create_error_response(request, u"Unable to parse problem id {}.".format(problem_to_reset)) + return _create_error_response(request, f"Unable to parse problem id {problem_to_reset}.") # check the user's access to this specific problem if not has_access(request.user, "staff", modulestore().get_item(usage_key)): - _create_error_response(request, u"User {} does not have permission to override scores for problem {}.".format( + _create_error_response(request, "User {} does not have permission to override scores for problem {}.".format( request.user.id, problem_to_reset )) @@ -1791,10 +1780,10 @@ def override_problem_score(request, course_id): # lint-amnesty, pylint: disable score, ) except NotImplementedError as exc: # if we try to override the score of a non-scorable block, catch it here - return _create_error_response(request, text_type(exc)) + return _create_error_response(request, str(exc)) except ValueError as exc: - return _create_error_response(request, text_type(exc)) + return _create_error_response(request, str(exc)) response_payload['task'] = TASK_SUBMISSION_OK return JsonResponse(response_payload) @@ -2004,7 +1993,7 @@ def list_report_downloads(request, course_id): response_payload = { 'downloads': [ - dict(name=name, url=url, link=HTML(u'{}').format(HTML(url), Text(name))) + dict(name=name, url=url, link=HTML('{}').format(HTML(url), Text(name))) for name, url in report_store.links_for(course_id) if report_name is None or name == report_name ] } @@ -2025,7 +2014,7 @@ def list_financial_report_downloads(_request, course_id): response_payload = { 'downloads': [ - dict(name=name, url=url, link=HTML(u'{}').format(HTML(url), Text(name))) + dict(name=name, url=url, link=HTML('{}').format(HTML(url), Text(name))) for name, url in report_store.links_for(course_id) ] } @@ -2151,7 +2140,7 @@ def list_forum_members(request, course_id): if rolename not in [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_GROUP_MODERATOR, FORUM_ROLE_COMMUNITY_TA]: return HttpResponseBadRequest(strip_tags( - u"Unrecognized rolename '{}'.".format(rolename) + f"Unrecognized rolename '{rolename}'." )) try: @@ -2176,7 +2165,7 @@ def list_forum_members(request, course_id): } response_payload = { - 'course_id': text_type(course_id), + 'course_id': str(course_id), rolename: list(map(extract_user_info, users)), 'division_scheme': course_discussion_settings.division_scheme, } @@ -2203,7 +2192,7 @@ def send_email(request, course_id): course_id = CourseKey.from_string(course_id) if not is_bulk_email_feature_enabled(course_id): - log.warning(u'Email is not enabled for course %s', course_id) + log.warning('Email is not enabled for course %s', course_id) return HttpResponseForbidden("Email is not enabled for this course.") targets = json.loads(request.POST.get("send_to")) @@ -2245,7 +2234,7 @@ def send_email(request, course_id): from_addr=from_addr ) except ValueError as err: - log.exception(u'Cannot create course email for course %s requested by user %s for targets %s', + log.exception('Cannot create course email for course %s requested by user %s for targets %s', course_id, request.user, targets) return HttpResponseBadRequest(repr(err)) @@ -2253,7 +2242,7 @@ def send_email(request, course_id): task_api.submit_bulk_course_email(request, course_id, email.id) response_payload = { - 'course_id': text_type(course_id), + 'course_id': str(course_id), 'success': True, } @@ -2309,7 +2298,7 @@ def update_forum_role_membership(request, course_id): if rolename not in [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_GROUP_MODERATOR, FORUM_ROLE_COMMUNITY_TA]: return HttpResponseBadRequest(strip_tags( - u"Unrecognized rolename '{}'.".format(rolename) + f"Unrecognized rolename '{rolename}'." )) user = get_student_from_identifier(unique_student_identifier) @@ -2320,7 +2309,7 @@ def update_forum_role_membership(request, course_id): return HttpResponseBadRequest("Role does not exist.") response_payload = { - 'course_id': text_type(course_id), + 'course_id': str(course_id), 'action': action, } return JsonResponse(response_payload) @@ -2347,9 +2336,9 @@ def _display_unit(unit): """ name = getattr(unit, 'display_name', None) if name: - return u'{0} ({1})'.format(name, text_type(unit.location)) + return '{} ({})'.format(name, str(unit.location)) else: - return text_type(unit.location) + return str(unit.location) @handle_dashboard_error @@ -2371,9 +2360,9 @@ def change_due_date(request, course_id): set_due_date_extension(course, unit, student, due_date, request.user, reason=reason) return JsonResponse(_( - u'Successfully changed due date for student {0} for {1} ' - u'to {2}').format(student.profile.name, _display_unit(unit), - due_date.strftime(u'%Y-%m-%d %H:%M'))) + 'Successfully changed due date for student {0} for {1} ' + 'to {2}').format(student.profile.name, _display_unit(unit), + due_date.strftime('%Y-%m-%d %H:%M'))) @handle_dashboard_error @@ -2400,11 +2389,11 @@ def reset_due_date(request, course_id): _("Successfully removed invalid due date extension (unit has no due date).") ) - original_due_date_str = original_due_date.strftime(u'%Y-%m-%d %H:%M') + original_due_date_str = original_due_date.strftime('%Y-%m-%d %H:%M') return JsonResponse(_( - u'Successfully reset due date for student {0} for {1} ' - u'to {2}').format(student.profile.name, _display_unit(unit), - original_due_date_str)) + 'Successfully reset due date for student {0} for {1} ' + 'to {2}').format(student.profile.name, _display_unit(unit), + original_due_date_str)) @handle_dashboard_error @@ -2470,9 +2459,9 @@ def _instructor_dash_url(course_key, section=None): unicode: The URL of a section in the instructor dashboard. """ - url = reverse('instructor_dashboard', kwargs={'course_id': six.text_type(course_key)}) + url = reverse('instructor_dashboard', kwargs={'course_id': str(course_key)}) if section is not None: - url += u'#view-{section}'.format(section=section) + url += f'#view-{section}' return url @@ -2526,9 +2515,9 @@ def mark_student_can_skip_entrance_exam(request, course_id): __, created = EntranceExamConfiguration.objects.get_or_create(user=student, course_id=course_id) if created: - message = _(u'This student (%s) will skip the entrance exam.') % student_identifier + message = _('This student (%s) will skip the entrance exam.') % student_identifier else: - message = _(u'This student (%s) is already allowed to skip the entrance exam.') % student_identifier + message = _('This student (%s) is already allowed to skip the entrance exam.') % student_identifier response_payload = { 'message': message, } @@ -2617,14 +2606,14 @@ def certificate_exception_view(request, course_id): try: certificate_exception, student = parse_request_data_and_get_user(request, course_key) except ValueError as error: - return JsonResponse({'success': False, 'message': text_type(error)}, status=400) + return JsonResponse({'success': False, 'message': str(error)}, status=400) # Add new Certificate Exception for the student passed in request data if request.method == 'POST': try: exception = add_certificate_exception(course_key, student, certificate_exception) except ValueError as error: - return JsonResponse({'success': False, 'message': text_type(error)}, status=400) + return JsonResponse({'success': False, 'message': str(error)}, status=400) return JsonResponse(exception) # Remove Certificate Exception for the student passed in request data @@ -2632,7 +2621,7 @@ def certificate_exception_view(request, course_id): try: remove_certificate_exception(course_key, student) except ValueError as error: - return JsonResponse({'success': False, 'message': text_type(error)}, status=400) + return JsonResponse({'success': False, 'message': str(error)}, status=400) return JsonResponse({}, status=204) @@ -2649,7 +2638,7 @@ def add_certificate_exception(course_key, student, certificate_exception): """ if CertificateWhitelist.get_certificate_white_list(course_key, student): raise ValueError( - _(u"Student (username/email={user}) already in certificate exception list.").format(user=student.username) + _("Student (username/email={user}) already in certificate exception list.").format(user=student.username) ) certificate_white_list, __ = CertificateWhitelist.objects.get_or_create( @@ -2660,7 +2649,7 @@ def add_certificate_exception(course_key, student, certificate_exception): 'notes': certificate_exception.get('notes', '') } ) - log.info(u'%s has been added to the whitelist in course %s', student.username, course_key) + log.info('%s has been added to the whitelist in course %s', student.username, course_key) generated_certificate = GeneratedCertificate.eligible_certificates.filter( user=student, @@ -2673,8 +2662,8 @@ def add_certificate_exception(course_key, student, certificate_exception): 'user_email': student.email, 'user_name': student.username, 'user_id': student.id, - 'certificate_generated': generated_certificate and generated_certificate.created_date.strftime(u"%B %d, %Y"), - 'created': certificate_white_list.created.strftime(u"%A, %B %d, %Y"), + 'certificate_generated': generated_certificate and generated_certificate.created_date.strftime("%B %d, %Y"), + 'created': certificate_white_list.created.strftime("%A, %B %d, %Y"), }) return exception @@ -2694,7 +2683,7 @@ def remove_certificate_exception(course_key, student): certificate_exception = CertificateWhitelist.objects.get(user=student, course_id=course_key) except ObjectDoesNotExist: raise ValueError( # lint-amnesty, pylint: disable=raise-missing-from - _(u'Certificate exception (user={user}) does not exist in certificate white list. ' + _('Certificate exception (user={user}) does not exist in certificate white list. ' 'Please refresh the page and try again.').format(user=student.username) ) @@ -2705,14 +2694,14 @@ def remove_certificate_exception(course_key, student): ) generated_certificate.invalidate() log.info( - u'Certificate invalidated for %s in course %s when removed from certificate exception list', + 'Certificate invalidated for %s in course %s when removed from certificate exception list', student.username, course_key ) except ObjectDoesNotExist: # Certificate has not been generated yet, so just remove the certificate exception from white list pass - log.info(u'%s has been removed from the whitelist in course %s', student.username, course_key) + log.info('%s has been removed from the whitelist in course %s', student.username, course_key) certificate_exception.delete() @@ -2744,7 +2733,7 @@ def parse_request_data(request): :return: dict object containing parsed json data. """ try: - data = json.loads(request.body.decode('utf8') or u'{}') + data = json.loads(request.body.decode('utf8') or '{}') except ValueError: raise ValueError(_('The record is not in the correct format. Please add a valid username or email address.')) # lint-amnesty, pylint: disable=raise-missing-from @@ -2763,13 +2752,13 @@ def get_student(username_or_email, course_key): try: student = get_user_by_username_or_email(username_or_email) except ObjectDoesNotExist: - raise ValueError(_(u"{user} does not exist in the LMS. Please check your spelling and retry.").format( # lint-amnesty, pylint: disable=raise-missing-from + raise ValueError(_("{user} does not exist in the LMS. Please check your spelling and retry.").format( # lint-amnesty, pylint: disable=raise-missing-from user=username_or_email )) # Make Sure the given student is enrolled in the course if not CourseEnrollment.is_enrolled(student, course_key): - raise ValueError(_(u"{user} is not enrolled in this course. Please check your spelling and retry.") + raise ValueError(_("{user} is not enrolled in this course. Please check your spelling and retry.") .format(user=username_or_email)) return student @@ -2847,7 +2836,7 @@ def generate_bulk_certificate_exceptions(request, course_id): """ inner method to build dict of csv data as row errors. """ - row_errors[key].append(_(u'user "{user}" in row# {row}').format(user=_user, row=row_count)) + row_errors[key].append(_('user "{user}" in row# {row}').format(user=_user, row=row_count)) if 'students_list' in request.FILES: try: @@ -2871,7 +2860,7 @@ def generate_bulk_certificate_exceptions(request, course_id): if len(student) != 2: if student: build_row_errors('data_format_error', student[user_index], row_num) - log.info(u'invalid data/format in csv row# %s', row_num) + log.info('invalid data/format in csv row# %s', row_num) continue user = student[user_index] @@ -2879,16 +2868,16 @@ def generate_bulk_certificate_exceptions(request, course_id): user = get_user_by_username_or_email(user) except ObjectDoesNotExist: build_row_errors('user_not_exist', user, row_num) - log.info(u'student %s does not exist', user) + log.info('student %s does not exist', user) else: if CertificateWhitelist.get_certificate_white_list(course_key, user): build_row_errors('user_already_white_listed', user, row_num) - log.warning(u'student %s already exist.', user.username) + log.warning('student %s already exist.', user.username) # make sure user is enrolled in course elif not CourseEnrollment.is_enrolled(user, course_key): build_row_errors('user_not_enrolled', user, row_num) - log.warning(u'student %s is not enrolled in course.', user.username) + log.warning('student %s is not enrolled in course.', user.username) else: CertificateWhitelist.objects.create( @@ -2897,7 +2886,7 @@ def generate_bulk_certificate_exceptions(request, course_id): whitelist=True, notes=student[notes_index] ) - success.append(_(u'user "{username}" in row# {row}').format(username=user.username, row=row_num)) + success.append(_('user "{username}" in row# {row}').format(username=user.username, row=row_num)) else: general_errors.append(_('File is not attached.')) @@ -2930,14 +2919,14 @@ def certificate_invalidation_view(request, course_id): certificate_invalidation_data = parse_request_data(request) certificate = validate_request_data_and_get_certificate(certificate_invalidation_data, course_key) except ValueError as error: - return JsonResponse({'message': text_type(error)}, status=400) + return JsonResponse({'message': str(error)}, status=400) # Invalidate certificate of the given student for the course course if request.method == 'POST': try: certificate_invalidation = invalidate_certificate(request, certificate, certificate_invalidation_data) except ValueError as error: - return JsonResponse({'message': text_type(error)}, status=400) + return JsonResponse({'message': str(error)}, status=400) return JsonResponse(certificate_invalidation) # Re-Validate student certificate for the course course @@ -2945,7 +2934,7 @@ def certificate_invalidation_view(request, course_id): try: re_validate_certificate(request, course_key, certificate) except ValueError as error: - return JsonResponse({'message': text_type(error)}, status=400) + return JsonResponse({'message': str(error)}, status=400) return JsonResponse({}, status=204) @@ -2964,7 +2953,7 @@ def invalidate_certificate(request, generated_certificate, certificate_invalidat generated_certificate.user, ): raise ValueError( - _(u"Certificate of {user} has already been invalidated. Please check your spelling and retry.").format( + _("Certificate of {user} has already been invalidated. Please check your spelling and retry.").format( user=generated_certificate.user.username, ) ) @@ -2972,7 +2961,7 @@ def invalidate_certificate(request, generated_certificate, certificate_invalidat # Verify that certificate user wants to invalidate is a valid one. if not generated_certificate.is_valid(): raise ValueError( - _(u"Certificate for student {user} is already invalid, kindly verify that certificate was generated " + _("Certificate for student {user} is already invalid, kindly verify that certificate was generated " "for this student and then proceed.").format(user=generated_certificate.user.username) ) @@ -2992,7 +2981,7 @@ def invalidate_certificate(request, generated_certificate, certificate_invalidat 'id': certificate_invalidation.id, 'user': certificate_invalidation.generated_certificate.user.username, 'invalidated_by': certificate_invalidation.invalidated_by.username, - 'created': certificate_invalidation.created.strftime(u"%B %d, %Y"), + 'created': certificate_invalidation.created.strftime("%B %d, %Y"), 'notes': certificate_invalidation.notes, } @@ -3048,7 +3037,7 @@ def validate_request_data_and_get_certificate(certificate_invalidation, course_k certificate = GeneratedCertificate.certificate_for_student(student, course_key) if not certificate: raise ValueError(_( - u"The student {student} does not have certificate for the course {course}. Kindly verify student " + "The student {student} does not have certificate for the course {course}. Kindly verify student " "username/email and the selected course are correct and try again." ).format(student=student.username, course=course_key.course)) return certificate diff --git a/lms/djangoapps/instructor/views/gradebook_api.py b/lms/djangoapps/instructor/views/gradebook_api.py index fb61ecdf1b..dcdfb2e1a9 100644 --- a/lms/djangoapps/instructor/views/gradebook_api.py +++ b/lms/djangoapps/instructor/views/gradebook_api.py @@ -12,8 +12,8 @@ from django.urls import reverse from django.views.decorators.cache import cache_control from opaque_keys.edx.keys import CourseKey -from lms.djangoapps.courseware.courses import get_course_with_access from common.djangoapps.edxmako.shortcuts import render_to_response +from lms.djangoapps.courseware.courses import get_course_with_access from lms.djangoapps.grades.api import CourseGradeFactory from lms.djangoapps.instructor.views.api import require_course_permission from xmodule.modulestore.django import modulestore diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py index 9286e719e8..29d83fdb9e 100644 --- a/lms/djangoapps/instructor/views/instructor_dashboard.py +++ b/lms/djangoapps/instructor/views/instructor_dashboard.py @@ -7,9 +7,9 @@ import datetime import logging import uuid from functools import reduce +from unittest.mock import patch import pytz -import six from django.conf import settings from django.contrib.auth.decorators import login_required from django.http import Http404, HttpResponseServerError @@ -22,17 +22,23 @@ from django.views.decorators.csrf import ensure_csrf_cookie from django.views.decorators.http import require_POST from edx_proctoring.api import does_backend_support_onboarding from edx_when.api import is_enabled_for_course -from mock import patch from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey -from six import text_type from six.moves.urllib.parse import urljoin # lint-amnesty, pylint: disable=unused-import from xblock.field_data import DictFieldData from xblock.fields import ScopeIds -from lms.djangoapps.bulk_email.api import is_bulk_email_feature_enabled from common.djangoapps.course_modes.models import CourseMode, CourseModesArchive from common.djangoapps.edxmako.shortcuts import render_to_response +from common.djangoapps.student.models import CourseEnrollment +from common.djangoapps.student.roles import ( + CourseFinanceAdminRole, + CourseInstructorRole, + CourseSalesAdminRole, + CourseStaffRole +) +from common.djangoapps.util.json_request import JsonResponse +from lms.djangoapps.bulk_email.api import is_bulk_email_feature_enabled from lms.djangoapps.certificates import api as certs_api from lms.djangoapps.certificates.models import ( CertificateGenerationConfiguration, @@ -55,19 +61,13 @@ from openedx.core.djangoapps.verified_track_content.models import VerifiedTrackC from openedx.core.djangolib.markup import HTML, Text from openedx.core.lib.url_utils import quote_slashes from openedx.core.lib.xblock_utils import wrap_xblock -from common.djangoapps.student.models import CourseEnrollment -from common.djangoapps.student.roles import ( - CourseFinanceAdminRole, CourseInstructorRole, - CourseSalesAdminRole, CourseStaffRole -) -from common.djangoapps.util.json_request import JsonResponse from xmodule.html_module import HtmlBlock from xmodule.modulestore.django import modulestore from xmodule.tabs import CourseTab -from .tools import get_units_with_due_date, title_or_url from .. import permissions from ..toggles import data_download_v2_is_enabled +from .tools import get_units_with_due_date, title_or_url log = logging.getLogger(__name__) @@ -111,7 +111,7 @@ def instructor_dashboard_2(request, course_id): # lint-amnesty, pylint: disable try: course_key = CourseKey.from_string(course_id) except InvalidKeyError: - log.error(u"Unable to find course with course key %s while loading the Instructor Dashboard.", course_id) + log.error("Unable to find course with course key %s while loading the Instructor Dashboard.", course_id) return HttpResponseServerError() course = get_course_by_id(course_key, depth=0) @@ -148,11 +148,11 @@ def instructor_dashboard_2(request, course_id): # lint-amnesty, pylint: disable analytics_dashboard_message = None if show_analytics_dashboard_message(course_key) and (access['staff'] or access['instructor']): # Construct a URL to the external analytics dashboard - analytics_dashboard_url = '{0}/courses/{1}'.format(settings.ANALYTICS_DASHBOARD_URL, six.text_type(course_key)) - link_start = HTML(u"").format(analytics_dashboard_url) + analytics_dashboard_url = '{}/courses/{}'.format(settings.ANALYTICS_DASHBOARD_URL, str(course_key)) + link_start = HTML("").format(analytics_dashboard_url) analytics_dashboard_message = _( - u"To gain insights into student enrollment and participation {link_start}" - u"visit {analytics_dashboard_name}, our new course analytics product{link_end}." + "To gain insights into student enrollment and participation {link_start}" + "visit {analytics_dashboard_name}, our new course analytics product{link_end}." ) analytics_dashboard_message = Text(analytics_dashboard_message).format( link_start=link_start, link_end=HTML(""), analytics_dashboard_name=settings.ANALYTICS_DASHBOARD_NAME) @@ -167,9 +167,9 @@ def instructor_dashboard_2(request, course_id): # lint-amnesty, pylint: disable course_mode_has_price = True elif len(paid_modes) > 1: log.error( - u"Course %s has %s course modes with payment options. Course must only have " - u"one paid course mode to enable eCommerce options.", - six.text_type(course_key), len(paid_modes) + "Course %s has %s course modes with payment options. Course must only have " + "one paid course mode to enable eCommerce options.", + str(course_key), len(paid_modes) ) if access['instructor'] and is_enabled_for_course(course_key): @@ -216,20 +216,20 @@ def instructor_dashboard_2(request, course_id): # lint-amnesty, pylint: disable certificate_white_list = CertificateWhitelist.get_certificate_white_list(course_key) generate_certificate_exceptions_url = reverse( 'generate_certificate_exceptions', - kwargs={'course_id': six.text_type(course_key), 'generate_for': ''} + kwargs={'course_id': str(course_key), 'generate_for': ''} ) generate_bulk_certificate_exceptions_url = reverse( 'generate_bulk_certificate_exceptions', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ) certificate_exception_view_url = reverse( 'certificate_exception_view', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ) certificate_invalidation_view_url = reverse( 'certificate_invalidation_view', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ) certificate_invalidations = CertificateInvalidation.get_certificate_invalidations(course_key) @@ -264,7 +264,7 @@ def instructor_dashboard_2(request, course_id): # lint-amnesty, pylint: disable def _section_special_exams(course, access): """ Provide data for the corresponding dashboard section """ - course_key = six.text_type(course.id) + course_key = str(course.id) proctoring_provider = course.proctoring_provider escalation_email = None if proctoring_provider == 'proctortrack': @@ -383,7 +383,7 @@ def set_course_mode_price(request, course_id): course_honor_mode = CourseMode.objects.filter(mode_slug='honor', course_id=course_key) if not course_honor_mode: return JsonResponse( - {'message': _(u"CourseMode with the mode slug({mode_slug}) DoesNotExist").format(mode_slug='honor')}, + {'message': _("CourseMode with the mode slug({mode_slug}) DoesNotExist").format(mode_slug='honor')}, status=400) # status code 400: Bad Request CourseModesArchive.objects.create( @@ -415,7 +415,7 @@ def _section_course_info(course, access): 'start_date': course.start, 'end_date': course.end, 'num_sections': len(course.children), - 'list_instructor_tasks_url': reverse('list_instructor_tasks', kwargs={'course_id': six.text_type(course_key)}), + 'list_instructor_tasks_url': reverse('list_instructor_tasks', kwargs={'course_id': str(course_key)}), } if settings.FEATURES.get('DISPLAY_ANALYTICS_ENROLLMENTS'): @@ -426,19 +426,19 @@ def _section_course_info(course, access): dashboard_link = _get_dashboard_link(course_key) # so we can use Text() here so it's not double-escaped and rendering HTML on the front-end message = Text( - _(u"Enrollment data is now available in {dashboard_link}.") + _("Enrollment data is now available in {dashboard_link}.") ).format(dashboard_link=dashboard_link) section_data['enrollment_message'] = message if settings.FEATURES.get('ENABLE_SYSADMIN_DASHBOARD'): section_data['detailed_gitlogs_url'] = reverse( 'gitlogs_detail', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ) try: sorted_cutoffs = sorted(list(course.grade_cutoffs.items()), key=lambda i: i[1], reverse=True) - advance = lambda memo, letter_score_tuple: u"{}: {}, ".format(letter_score_tuple[0], letter_score_tuple[1]) \ + advance = lambda memo, letter_score_tuple: "{}: {}, ".format(letter_score_tuple[0], letter_score_tuple[1]) \ + memo section_data['grade_cutoffs'] = reduce(advance, sorted_cutoffs, "")[:-2] except Exception: # pylint: disable=broad-except @@ -464,25 +464,25 @@ def _section_membership(course, access): 'section_display_name': _('Membership'), 'access': access, 'ccx_is_enabled': ccx_enabled, - 'enroll_button_url': reverse('students_update_enrollment', kwargs={'course_id': six.text_type(course_key)}), - 'unenroll_button_url': reverse('students_update_enrollment', kwargs={'course_id': six.text_type(course_key)}), + 'enroll_button_url': reverse('students_update_enrollment', kwargs={'course_id': str(course_key)}), + 'unenroll_button_url': reverse('students_update_enrollment', kwargs={'course_id': str(course_key)}), 'upload_student_csv_button_url': reverse( 'register_and_enroll_students', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ), 'modify_beta_testers_button_url': reverse( 'bulk_beta_modify_access', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ), 'list_course_role_members_url': reverse( 'list_course_role_members', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ), - 'modify_access_url': reverse('modify_access', kwargs={'course_id': six.text_type(course_key)}), - 'list_forum_members_url': reverse('list_forum_members', kwargs={'course_id': six.text_type(course_key)}), + 'modify_access_url': reverse('modify_access', kwargs={'course_id': str(course_key)}), + 'list_forum_members_url': reverse('list_forum_members', kwargs={'course_id': str(course_key)}), 'update_forum_role_membership_url': reverse( 'update_forum_role_membership', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ), 'enrollment_role_choices': enrollment_role_choices, 'is_reason_field_enabled': configuration_helpers.get_value('ENABLE_MANUAL_ENROLLMENT_REASON_FIELD', False) @@ -501,12 +501,12 @@ def _section_cohort_management(course, access): 'ccx_is_enabled': ccx_enabled, 'course_cohort_settings_url': reverse( 'course_cohort_settings', - kwargs={'course_key_string': six.text_type(course_key)} + kwargs={'course_key_string': str(course_key)} ), - 'cohorts_url': reverse('cohorts', kwargs={'course_key_string': six.text_type(course_key)}), - 'upload_cohorts_csv_url': reverse('add_users_to_cohorts', kwargs={'course_id': six.text_type(course_key)}), + 'cohorts_url': reverse('cohorts', kwargs={'course_key_string': str(course_key)}), + 'upload_cohorts_csv_url': reverse('add_users_to_cohorts', kwargs={'course_id': str(course_key)}), 'verified_track_cohorting_url': reverse( - 'verified_track_cohorting', kwargs={'course_key_string': six.text_type(course_key)} + 'verified_track_cohorting', kwargs={'course_key_string': str(course_key)} ), } return section_data @@ -521,10 +521,10 @@ def _section_discussions_management(course, access): # lint-amnesty, pylint: di 'section_display_name': _('Discussions'), 'is_hidden': (not is_course_cohorted(course_key) and CourseDiscussionSettings.ENROLLMENT_TRACK not in enrollment_track_schemes), - 'discussion_topics_url': reverse('discussion_topics', kwargs={'course_key_string': six.text_type(course_key)}), + 'discussion_topics_url': reverse('discussion_topics', kwargs={'course_key_string': str(course_key)}), 'course_discussion_settings': reverse( 'course_discussions_settings', - kwargs={'course_key_string': six.text_type(course_key)} + kwargs={'course_key_string': str(course_key)} ), } return section_data @@ -542,40 +542,40 @@ def _section_student_admin(course, access): 'is_small_course': is_small_course, 'get_student_enrollment_status_url': reverse( 'get_student_enrollment_status', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ), 'get_student_progress_url_url': reverse( 'get_student_progress_url', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ), - 'enrollment_url': reverse('students_update_enrollment', kwargs={'course_id': six.text_type(course_key)}), + 'enrollment_url': reverse('students_update_enrollment', kwargs={'course_id': str(course_key)}), 'reset_student_attempts_url': reverse( 'reset_student_attempts', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ), 'reset_student_attempts_for_entrance_exam_url': reverse( 'reset_student_attempts_for_entrance_exam', - kwargs={'course_id': six.text_type(course_key)}, + kwargs={'course_id': str(course_key)}, ), - 'rescore_problem_url': reverse('rescore_problem', kwargs={'course_id': six.text_type(course_key)}), + 'rescore_problem_url': reverse('rescore_problem', kwargs={'course_id': str(course_key)}), 'override_problem_score_url': reverse( 'override_problem_score', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ), - 'rescore_entrance_exam_url': reverse('rescore_entrance_exam', kwargs={'course_id': six.text_type(course_key)}), + 'rescore_entrance_exam_url': reverse('rescore_entrance_exam', kwargs={'course_id': str(course_key)}), 'student_can_skip_entrance_exam_url': reverse( 'mark_student_can_skip_entrance_exam', - kwargs={'course_id': six.text_type(course_key)}, + kwargs={'course_id': str(course_key)}, ), - 'list_instructor_tasks_url': reverse('list_instructor_tasks', kwargs={'course_id': six.text_type(course_key)}), + 'list_instructor_tasks_url': reverse('list_instructor_tasks', kwargs={'course_id': str(course_key)}), 'list_entrace_exam_instructor_tasks_url': reverse( 'list_entrance_exam_instructor_tasks', - kwargs={'course_id': six.text_type(course_key)} + kwargs={'course_id': str(course_key)} ), - 'spoc_gradebook_url': reverse('spoc_gradebook', kwargs={'course_id': six.text_type(course_key)}), + 'spoc_gradebook_url': reverse('spoc_gradebook', kwargs={'course_id': str(course_key)}), } if is_writable_gradebook_enabled(course_key) and settings.WRITABLE_GRADEBOOK_URL: - section_data['writable_gradebook_url'] = '{}/{}'.format(settings.WRITABLE_GRADEBOOK_URL, text_type(course_key)) + section_data['writable_gradebook_url'] = '{}/{}'.format(settings.WRITABLE_GRADEBOOK_URL, str(course_key)) return section_data @@ -584,14 +584,14 @@ def _section_extensions(course): section_data = { 'section_key': 'extensions', 'section_display_name': _('Extensions'), - 'units_with_due_dates': [(title_or_url(unit), six.text_type(unit.location)) + 'units_with_due_dates': [(title_or_url(unit), str(unit.location)) for unit in get_units_with_due_date(course)], - 'change_due_date_url': reverse('change_due_date', kwargs={'course_id': six.text_type(course.id)}), - 'reset_due_date_url': reverse('reset_due_date', kwargs={'course_id': six.text_type(course.id)}), - 'show_unit_extensions_url': reverse('show_unit_extensions', kwargs={'course_id': six.text_type(course.id)}), + 'change_due_date_url': reverse('change_due_date', kwargs={'course_id': str(course.id)}), + 'reset_due_date_url': reverse('reset_due_date', kwargs={'course_id': str(course.id)}), + 'show_unit_extensions_url': reverse('show_unit_extensions', kwargs={'course_id': str(course.id)}), 'show_student_extensions_url': reverse( 'show_student_extensions', - kwargs={'course_id': six.text_type(course.id)} + kwargs={'course_id': str(course.id)} ), } return section_data @@ -611,30 +611,30 @@ def _section_data_download(course, access): 'section_display_name': _('Data Download'), 'access': access, 'show_generate_proctored_exam_report_button': show_proctored_report_button, - 'get_problem_responses_url': reverse('get_problem_responses', kwargs={'course_id': six.text_type(course_key)}), - 'get_grading_config_url': reverse('get_grading_config', kwargs={'course_id': six.text_type(course_key)}), - 'get_students_features_url': reverse('get_students_features', kwargs={'course_id': six.text_type(course_key)}), + 'get_problem_responses_url': reverse('get_problem_responses', kwargs={'course_id': str(course_key)}), + 'get_grading_config_url': reverse('get_grading_config', kwargs={'course_id': str(course_key)}), + 'get_students_features_url': reverse('get_students_features', kwargs={'course_id': str(course_key)}), 'get_issued_certificates_url': reverse( - 'get_issued_certificates', kwargs={'course_id': six.text_type(course_key)} + 'get_issued_certificates', kwargs={'course_id': str(course_key)} ), 'get_students_who_may_enroll_url': reverse( - 'get_students_who_may_enroll', kwargs={'course_id': six.text_type(course_key)} + 'get_students_who_may_enroll', kwargs={'course_id': str(course_key)} ), - 'get_anon_ids_url': reverse('get_anon_ids', kwargs={'course_id': six.text_type(course_key)}), + 'get_anon_ids_url': reverse('get_anon_ids', kwargs={'course_id': str(course_key)}), 'list_proctored_results_url': reverse( - 'get_proctored_exam_results', kwargs={'course_id': six.text_type(course_key)} + 'get_proctored_exam_results', kwargs={'course_id': str(course_key)} ), - 'list_instructor_tasks_url': reverse('list_instructor_tasks', kwargs={'course_id': six.text_type(course_key)}), - 'list_report_downloads_url': reverse('list_report_downloads', kwargs={'course_id': six.text_type(course_key)}), - 'calculate_grades_csv_url': reverse('calculate_grades_csv', kwargs={'course_id': six.text_type(course_key)}), - 'problem_grade_report_url': reverse('problem_grade_report', kwargs={'course_id': six.text_type(course_key)}), + 'list_instructor_tasks_url': reverse('list_instructor_tasks', kwargs={'course_id': str(course_key)}), + 'list_report_downloads_url': reverse('list_report_downloads', kwargs={'course_id': str(course_key)}), + 'calculate_grades_csv_url': reverse('calculate_grades_csv', kwargs={'course_id': str(course_key)}), + 'problem_grade_report_url': reverse('problem_grade_report', kwargs={'course_id': str(course_key)}), 'course_has_survey': True if course.course_survey_name else False, # lint-amnesty, pylint: disable=simplifiable-if-expression 'course_survey_results_url': reverse( - 'get_course_survey_results', kwargs={'course_id': six.text_type(course_key)} + 'get_course_survey_results', kwargs={'course_id': str(course_key)} ), - 'export_ora2_data_url': reverse('export_ora2_data', kwargs={'course_id': six.text_type(course_key)}), + 'export_ora2_data_url': reverse('export_ora2_data', kwargs={'course_id': str(course_key)}), 'export_ora2_submission_files_url': reverse( - 'export_ora2_submission_files', kwargs={'course_id': six.text_type(course_key)} + 'export_ora2_submission_files', kwargs={'course_id': str(course_key)} ), } if not access.get('data_researcher'): @@ -666,8 +666,8 @@ def _section_send_email(course, access): fragment = course.system.render(html_module, 'studio_view') fragment = wrap_xblock( 'LmsRuntime', html_module, 'studio_view', fragment, None, - extra_data={"course-id": six.text_type(course_key)}, - usage_id_serializer=lambda usage_id: quote_slashes(six.text_type(usage_id)), + extra_data={"course-id": str(course_key)}, + usage_id_serializer=lambda usage_id: quote_slashes(str(usage_id)), # Generate a new request_token here at random, because this module isn't connected to any other # xblock rendering. request_token=uuid.uuid1().hex @@ -683,19 +683,19 @@ def _section_send_email(course, access): 'section_key': 'send_email', 'section_display_name': _('Email'), 'access': access, - 'send_email': reverse('send_email', kwargs={'course_id': six.text_type(course_key)}), + 'send_email': reverse('send_email', kwargs={'course_id': str(course_key)}), 'editor': email_editor, 'cohorts': cohorts, 'course_modes': course_modes, 'default_cohort_name': DEFAULT_COHORT_NAME, 'list_instructor_tasks_url': reverse( - 'list_instructor_tasks', kwargs={'course_id': six.text_type(course_key)} + 'list_instructor_tasks', kwargs={'course_id': str(course_key)} ), 'email_background_tasks_url': reverse( - 'list_background_email_tasks', kwargs={'course_id': six.text_type(course_key)} + 'list_background_email_tasks', kwargs={'course_id': str(course_key)} ), 'email_content_history_url': reverse( - 'list_email_content', kwargs={'course_id': six.text_type(course_key)} + 'list_email_content', kwargs={'course_id': str(course_key)} ), } return section_data @@ -703,8 +703,8 @@ def _section_send_email(course, access): def _get_dashboard_link(course_key): """ Construct a URL to the external analytics dashboard """ - analytics_dashboard_url = u'{0}/courses/{1}'.format(settings.ANALYTICS_DASHBOARD_URL, six.text_type(course_key)) - link = HTML(u"{1}").format( + analytics_dashboard_url = '{}/courses/{}'.format(settings.ANALYTICS_DASHBOARD_URL, str(course_key)) + link = HTML("{1}").format( analytics_dashboard_url, settings.ANALYTICS_DASHBOARD_NAME ) return link @@ -716,7 +716,7 @@ def _section_analytics(course, access): 'section_key': 'instructor_analytics', 'section_display_name': _('Analytics'), 'access': access, - 'course_id': six.text_type(course.id), + 'course_id': str(course.id), } return section_data @@ -729,8 +729,8 @@ def _section_open_response_assessment(request, course, openassessment_blocks, ac parents = {} for block in openassessment_blocks: - block_parent_id = six.text_type(block.parent) - result_item_id = six.text_type(block.location) + block_parent_id = str(block.parent) + result_item_id = str(block.location) if block_parent_id not in parents: parents[block_parent_id] = modulestore().get_item(block.parent) assessment_name = _("Team") + " : " + block.display_name if block.teams_enabled else block.display_name @@ -747,7 +747,7 @@ def _section_open_response_assessment(request, course, openassessment_blocks, ac openassessment_block = openassessment_blocks[0] block, __ = get_module_by_usage_id( - request, six.text_type(course_key), six.text_type(openassessment_block.location), + request, str(course_key), str(openassessment_block.location), disable_staff_debug_info=True, course=course ) section_data = { @@ -758,7 +758,7 @@ def _section_open_response_assessment(request, course, openassessment_blocks, ac 'section_key': 'open_response_assessment', 'section_display_name': _('Open Responses'), 'access': access, - 'course_id': six.text_type(course_key), + 'course_id': str(course_key), } return section_data diff --git a/lms/djangoapps/instructor/views/instructor_task_helpers.py b/lms/djangoapps/instructor/views/instructor_task_helpers.py index e7eb12d234..7ea28b10fd 100644 --- a/lms/djangoapps/instructor/views/instructor_task_helpers.py +++ b/lms/djangoapps/instructor/views/instructor_task_helpers.py @@ -7,13 +7,12 @@ tasks. import json import logging -import six from django.utils.translation import ugettext as _ from django.utils.translation import ungettext +from common.djangoapps.util.date_utils import get_default_time_display from lms.djangoapps.bulk_email.models import CourseEmail from lms.djangoapps.instructor_task.views import get_task_completion_info -from common.djangoapps.util.date_utils import get_default_time_display log = logging.getLogger(__name__) @@ -56,7 +55,7 @@ def extract_email_features(email_task): try: task_input_information = json.loads(email_task.task_input) except ValueError: - log.error(u"Could not parse task input as valid json; task input: %s", email_task.task_input) + log.error("Could not parse task input as valid json; task input: %s", email_task.task_input) return email_error_information() email = CourseEmail.objects.get(id=task_input_information['email_id']) @@ -66,7 +65,7 @@ def extract_email_features(email_task): 'requester': str(email_task.requester), } features = ['subject', 'html_message', 'id'] - email_info = {feature: six.text_type(getattr(email, feature)) for feature in features} + email_info = {feature: str(getattr(email, feature)) for feature in features} # Pass along email as an object with the information we desire email_feature_dict['email'] = email_info @@ -77,13 +76,13 @@ def extract_email_features(email_task): try: task_output = json.loads(email_task.task_output) except ValueError: - log.error(u"Could not parse task output as valid json; task output: %s", email_task.task_output) + log.error("Could not parse task output as valid json; task output: %s", email_task.task_output) else: if 'succeeded' in task_output and task_output['succeeded'] > 0: num_emails = task_output['succeeded'] number_sent = ungettext( - u"{num_emails} sent", - u"{num_emails} sent", + "{num_emails} sent", + "{num_emails} sent", num_emails ).format(num_emails=num_emails) @@ -91,8 +90,8 @@ def extract_email_features(email_task): num_emails = task_output['failed'] number_sent += ", " number_sent += ungettext( - u"{num_emails} failed", - u"{num_emails} failed", + "{num_emails} failed", + "{num_emails} failed", num_emails ).format(num_emails=num_emails) @@ -124,7 +123,7 @@ def extract_task_features(task): try: task_output = json.loads(task.task_output) except ValueError: - log.error(u"Could not parse task output as valid json; task output: %s", task.task_output) + log.error("Could not parse task output as valid json; task output: %s", task.task_output) else: if 'duration_ms' in task_output: duration_sec = int(task_output['duration_ms'] / 1000.0) diff --git a/lms/djangoapps/instructor/views/tools.py b/lms/djangoapps/instructor/views/tools.py index cca5b91778..ea40d4d536 100644 --- a/lms/djangoapps/instructor/views/tools.py +++ b/lms/djangoapps/instructor/views/tools.py @@ -7,18 +7,15 @@ import json import operator import dateutil -import six from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.http import HttpResponseBadRequest from django.utils.translation import ugettext as _ from edx_when import api from opaque_keys.edx.keys import UsageKey from pytz import UTC -from six import string_types, text_type -from six.moves import zip +from common.djangoapps.student.models import CourseEnrollment, get_user_by_username_or_email from openedx.core.djangoapps.schedules.models import Schedule -from common.djangoapps.student.models import get_user_by_username_or_email, CourseEnrollment class DashboardError(Exception): @@ -29,7 +26,7 @@ class DashboardError(Exception): """ Generate an instance of HttpResponseBadRequest for this error. """ - error = six.text_type(self) + error = str(self) return HttpResponseBadRequest(json.dumps({'error': error})) @@ -52,7 +49,7 @@ def handle_dashboard_error(view): def strip_if_string(value): - if isinstance(value, string_types): + if isinstance(value, str): return value.strip() return value @@ -80,7 +77,7 @@ def require_student_from_identifier(unique_student_identifier): return get_student_from_identifier(unique_student_identifier) except User.DoesNotExist: raise DashboardError( # lint-amnesty, pylint: disable=raise-missing-from - _(u"Could not find student matching identifier: {student_identifier}").format( + _("Could not find student matching identifier: {student_identifier}").format( student_identifier=unique_student_identifier ) ) @@ -107,7 +104,7 @@ def find_unit(course, url): """ Find node in course tree for url. """ - if text_type(node.location) == url: + if str(node.location) == url: return node for child in node.get_children(): found = find(child, url) @@ -117,7 +114,7 @@ def find_unit(course, url): unit = find(course, url) if unit is None: - raise DashboardError(_(u"Couldn't find module for url: {0}").format(url)) + raise DashboardError(_("Couldn't find module for url: {0}").format(url)) return unit @@ -157,7 +154,7 @@ def title_or_url(node): """ title = getattr(node, 'display_name', None) if not title: - title = text_type(node.location) + title = str(node.location) return title @@ -169,7 +166,7 @@ def set_due_date_extension(course, unit, student, due_date, actor=None, reason=' DashboardError if the unit or extended, due date is invalid or user is not enrolled in the course. """ - mode, __ = CourseEnrollment.enrollment_mode_for_user(user=student, course_id=six.text_type(course.id)) + mode, __ = CourseEnrollment.enrollment_mode_for_user(user=student, course_id=str(course.id)) if not mode: raise DashboardError(_("Could not find student enrollment in the course.")) @@ -196,10 +193,10 @@ def set_due_date_extension(course, unit, student, due_date, actor=None, reason=' try: api.set_date_for_block(course.id, block.location, 'due', due_date, user=student, reason=reason, actor=actor) - except api.MissingDateError: - raise DashboardError(_(u"Unit {0} has no due date to extend.").format(unit.location)) - except api.InvalidDateError: - raise DashboardError(_("An extended due date must be later than the original due date.")) + except api.MissingDateError as ex: + raise DashboardError(_("Unit {0} has no due date to extend.").format(unit.location)) from ex + except api.InvalidDateError as ex: + raise DashboardError(_("An extended due date must be later than the original due date.")) from ex else: api.set_date_for_block(course.id, block.location, 'due', None, user=student, reason=reason, actor=actor) @@ -212,12 +209,12 @@ def dump_module_extensions(course, unit): header = [_("Username"), _("Full Name"), _("Extended Due Date")] data = [] for username, fullname, due_date in api.get_overrides_for_block(course.id, unit.location): - due_date = due_date.strftime(u'%Y-%m-%d %H:%M') + due_date = due_date.strftime('%Y-%m-%d %H:%M') data.append(dict(list(zip(header, (username, fullname, due_date))))) data.sort(key=operator.itemgetter(_("Username"))) return { "header": header, - "title": _(u"Users with due date extensions for {0}").format( + "title": _("Users with due date extensions for {0}").format( title_or_url(unit)), "data": data } @@ -238,13 +235,13 @@ def dump_student_extensions(course, student): if location not in units: continue due = override['actual_date'] - due = due.strftime(u"%Y-%m-%d %H:%M") + due = due.strftime("%Y-%m-%d %H:%M") title = title_or_url(units[location]) data.append(dict(list(zip(header, (title, due))))) data.sort(key=operator.itemgetter(_("Unit"))) return { "header": header, - "title": _(u"Due date extensions for {0} {1} ({2})").format( + "title": _("Due date extensions for {0} {1} ({2})").format( student.first_name, student.last_name, student.username), "data": data}