pyupgrade on LMS instructor app (#26533)

This commit is contained in:
M. Zulqarnain
2021-02-22 12:58:35 +05:00
committed by GitHub
parent f4a5af29d3
commit 36748ff78f
25 changed files with 846 additions and 877 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 = '<button type="button" class="btn-link send_email" data-section="send_email">Email</button>'
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 = '<button type="button" class="btn-link send_email" data-section="send_email">Email</button>'
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 = '<button type="button" class="btn-link send_email" data-section="send_email">Email</button>'
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

View File

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

View File

@@ -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 = '<button type="button" class="btn-link special_exams" data-section="special_exams">Special Exams</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):
"""

View File

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

View File

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

View File

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

View File

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

View File

@@ -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='<script>alert("XSS")</script>'
@@ -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 <a href="http://example.com/courses/{}" ' \
'rel="noopener" target="_blank">Example</a>.'.format(text_type(self.course.id))
return 'Enrollment data is now available in <a href="http://example.com/courses/{}" ' \
'rel="noopener" target="_blank">Example</a>.'.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 <a href="http://example.com/courses/{}" ' \
'rel="noopener" target="_blank">Example</a>.'.format(text_type(self.course.id))
return 'For analytics about your course, go to <a href="http://example.com/courses/{}" ' \
'rel="noopener" target="_blank">Example</a>.'.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='<script>alert("XSS")</script>',
@@ -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'},

View File

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

View File

@@ -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 = {}

View File

@@ -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'<a href="{}">{}</a>').format(HTML(url), Text(name)))
dict(name=name, url=url, link=HTML('<a href="{}">{}</a>').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'<a href="{}">{}</a>').format(HTML(url), Text(name)))
dict(name=name, url=url, link=HTML('<a href="{}">{}</a>').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

View File

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

View File

@@ -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"<a href=\"{}\" rel=\"noopener\" target=\"_blank\">").format(analytics_dashboard_url)
analytics_dashboard_url = '{}/courses/{}'.format(settings.ANALYTICS_DASHBOARD_URL, str(course_key))
link_start = HTML("<a href=\"{}\" rel=\"noopener\" target=\"_blank\">").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("</a>"), 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"<a href=\"{0}\" rel=\"noopener\" target=\"_blank\">{1}</a>").format(
analytics_dashboard_url = '{}/courses/{}'.format(settings.ANALYTICS_DASHBOARD_URL, str(course_key))
link = HTML("<a href=\"{0}\" rel=\"noopener\" target=\"_blank\">{1}</a>").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

View File

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

View File

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