pyupgrade on LMS instructor app (#26533)
This commit is contained in:
@@ -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}'")
|
||||
|
||||
@@ -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'},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'):
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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'},
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user