refactor: ran pyupgrade on lms/djangoapps (#26733)

ran pyupgrade on bulk_enroll & ccx apps.
This commit is contained in:
Usama Sadiq
2021-03-04 16:00:19 +05:00
committed by GitHub
parent ce0fc3804d
commit 519384edca
30 changed files with 230 additions and 259 deletions

View File

@@ -6,7 +6,6 @@ Serializers for Bulk Enrollment.
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from rest_framework import serializers
from six.moves import zip
from openedx.core.djangoapps.course_groups.cohorts import is_cohort_exists
@@ -47,7 +46,7 @@ class BulkEnrollmentSerializer(serializers.Serializer): # lint-amnesty, pylint:
try:
CourseKey.from_string(course)
except InvalidKeyError:
raise serializers.ValidationError(u"Course key not valid: {}".format(course)) # lint-amnesty, pylint: disable=raise-missing-from
raise serializers.ValidationError(f"Course key not valid: {course}") # lint-amnesty, pylint: disable=raise-missing-from
return value
def validate(self, attrs):
@@ -63,7 +62,7 @@ class BulkEnrollmentSerializer(serializers.Serializer): # lint-amnesty, pylint:
for course_id, cohort_name in zip(attrs['courses'], attrs['cohorts']):
if not is_cohort_exists(course_key=CourseKey.from_string(course_id), name=cohort_name):
raise serializers.ValidationError(u"cohort {cohort_name} not found in course {course_id}.".format(
raise serializers.ValidationError("cohort {cohort_name} not found in course {course_id}.".format(
cohort_name=cohort_name, course_id=course_id)
)

View File

@@ -6,7 +6,6 @@ Tests for the Bulk Enrollment views.
import json
import ddt
import six
from django.conf import settings
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.core import mail
@@ -15,14 +14,19 @@ from django.urls import reverse
from opaque_keys.edx.keys import CourseKey
from rest_framework.test import APIRequestFactory, APITestCase, force_authenticate
from common.djangoapps.student.models import ( # lint-amnesty, pylint: disable=line-too-long
ENROLLED_TO_UNENROLLED,
UNENROLLED_TO_ENROLLED,
CourseEnrollment,
ManualEnrollmentAudit
)
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.bulk_enroll.serializers import BulkEnrollmentSerializer
from lms.djangoapps.bulk_enroll.views import BulkEnrollView
from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase
from openedx.core.djangoapps.course_groups.cohorts import get_cohort_id
from openedx.core.djangoapps.course_groups.tests.helpers import config_course_cohorts
from openedx.core.djangoapps.site_configuration.helpers import get_value as get_site_value
from common.djangoapps.student.models import ENROLLED_TO_UNENROLLED, UNENROLLED_TO_ENROLLED, CourseEnrollment, ManualEnrollmentAudit # lint-amnesty, pylint: disable=line-too-long
from common.djangoapps.student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -40,7 +44,7 @@ class BulkEnrollmentTest(ModuleStoreTestCase, LoginEnrollmentTestCase, APITestCa
def setUp(self):
""" Create a course and user, then log in. """
super(BulkEnrollmentTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.view = BulkEnrollView.as_view()
self.request_factory = APIRequestFactory()
@@ -54,7 +58,7 @@ class BulkEnrollmentTest(ModuleStoreTestCase, LoginEnrollmentTestCase, APITestCa
)
self.course = CourseFactory.create()
self.course_key = six.text_type(self.course.id)
self.course_key = str(self.course.id)
self.enrolled_student = UserFactory(username='EnrolledStudent', first_name='Enrolled', last_name='Student')
CourseEnrollment.enroll(
self.enrolled_student,
@@ -68,8 +72,8 @@ class BulkEnrollmentTest(ModuleStoreTestCase, LoginEnrollmentTestCase, APITestCa
'SITE_NAME',
settings.SITE_NAME
)
self.about_path = '/courses/{}/about'.format(self.course.id)
self.course_path = '/courses/{}/'.format(self.course.id)
self.about_path = f'/courses/{self.course.id}/about'
self.course_path = f'/courses/{self.course.id}/'
def request_bulk_enroll(self, data=None, use_json=False, **extra):
""" Make an authenticated request to the bulk enrollment API. """
@@ -368,7 +372,7 @@ class BulkEnrollmentTest(ModuleStoreTestCase, LoginEnrollmentTestCase, APITestCa
})
self.assertContains(
response,
u'cohort {cohort_name} not found in course {course_id}.'.format(
'cohort {cohort_name} not found in course {course_id}.'.format(
cohort_name='cohort1', course_id=self.course_key
),
status_code=400,

View File

@@ -13,6 +13,7 @@ from rest_framework.response import Response
from rest_framework.views import APIView
from six.moves import zip_longest
from common.djangoapps.util.disable_rate_limit import can_disable_rate_limit
from lms.djangoapps.bulk_enroll.serializers import BulkEnrollmentSerializer
from lms.djangoapps.instructor.views.api import students_update_enrollment
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, get_cohort_by_name
@@ -20,7 +21,6 @@ from openedx.core.djangoapps.course_groups.models import CourseUserGroup
from openedx.core.djangoapps.enrollments.views import EnrollmentUserThrottle
from openedx.core.lib.api.authentication import BearerAuthentication
from openedx.core.lib.api.permissions import IsStaff
from common.djangoapps.util.disable_rate_limit import can_disable_rate_limit
@can_disable_rate_limit

View File

@@ -14,7 +14,7 @@ class CCXAPIPagination(DefaultPagination):
"""
Annotate the response with pagination information.
"""
response = super(CCXAPIPagination, self).get_paginated_response(data) # lint-amnesty, pylint: disable=super-with-arguments
response = super().get_paginated_response(data)
# Add the current page to the response.
response.data["current_page"] = self.page.number

View File

@@ -1,7 +1,6 @@
""" CCX API v0 Serializers. """
import six
from ccx_keys.locator import CCXLocator
from rest_framework import serializers
@@ -21,7 +20,7 @@ class CCXCourseSerializer(serializers.ModelSerializer):
max_students_allowed = serializers.IntegerField(source='max_student_enrollments_allowed')
course_modules = serializers.SerializerMethodField()
class Meta(object):
class Meta:
model = CustomCourseForEdX
fields = (
"ccx_course_id",
@@ -45,7 +44,7 @@ class CCXCourseSerializer(serializers.ModelSerializer):
"""
Getter for the CCX Course ID
"""
return six.text_type(CCXLocator.from_course_locator(obj.course.id, obj.id))
return str(CCXLocator.from_course_locator(obj.course.id, obj.id))
@staticmethod
def get_course_modules(obj):

View File

@@ -7,10 +7,10 @@ import json
import math
import string
from datetime import timedelta
from unittest import mock
import urllib
import pytest
import ddt
import mock
import six
from ccx_keys.locator import CCXLocator
from django.conf import settings
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
@@ -20,8 +20,10 @@ from oauth2_provider import models as dot_models
from opaque_keys.edx.keys import CourseKey
from rest_framework import status
from rest_framework.test import APITestCase
from six.moves import range, zip
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole
from common.djangoapps.student.tests.factories import AdminFactory, UserFactory
from lms.djangoapps.ccx.api.v0 import views
from lms.djangoapps.ccx.models import CcxFieldOverride, CustomCourseForEdX
from lms.djangoapps.ccx.overrides import override_field_for_ccx
@@ -30,9 +32,6 @@ from lms.djangoapps.ccx.utils import ccx_course as ccx_course_cm
from lms.djangoapps.courseware import courses
from lms.djangoapps.instructor.access import allow_access, list_with_level
from lms.djangoapps.instructor.enrollment import enroll_email, get_email_params
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole
from common.djangoapps.student.tests.factories import AdminFactory, UserFactory
USER_PASSWORD = 'test'
@@ -43,16 +42,16 @@ class CcxRestApiTest(CcxTestCase, APITestCase):
"""
@classmethod
def setUpClass(cls):
super(CcxRestApiTest, cls).setUpClass()
super().setUpClass()
def setUp(self):
"""
Set up tests
"""
super(CcxRestApiTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# add some info about the course for easy access
self.master_course_key = self.course.location.course_key
self.master_course_key_str = six.text_type(self.master_course_key)
self.master_course_key_str = str(self.master_course_key)
# OAUTH2 setup
# create a specific user for the application
self.app_user = app_user = UserFactory(
@@ -96,7 +95,7 @@ class CcxRestApiTest(CcxTestCase, APITestCase):
token='16MGyP3OaQYHmpT1lK7Q6MMNAZsjwF'
)
auth_header_oauth2_provider = u"Bearer {0}".format(auth_oauth2_provider)
auth_header_oauth2_provider = f"Bearer {auth_oauth2_provider}"
return auth_header_oauth2_provider
@@ -118,7 +117,7 @@ class CcxRestApiTest(CcxTestCase, APITestCase):
assert 'field_errors' in resp_obj.data
# restructure the error dictionary for a easier comparison
resp_dict_error = {}
for field_name, error_dict in six.iteritems(resp_obj.data['field_errors']):
for field_name, error_dict in resp_obj.data['field_errors'].items():
resp_dict_error[field_name] = error_dict.get('error_code', '')
assert expected_field_errors == resp_dict_error
@@ -132,17 +131,17 @@ class CcxListTest(CcxRestApiTest):
@classmethod
def setUpClass(cls):
super(CcxListTest, cls).setUpClass()
super().setUpClass()
def setUp(self):
"""
Set up tests
"""
super(CcxListTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.list_url = reverse('ccx_api:v0:ccx:list')
self.list_url_master_course = six.moves.urllib.parse.urljoin(
self.list_url_master_course = urllib.parse.urljoin(
self.list_url,
'?master_course_id={0}'.format(six.moves.urllib.parse.quote_plus(self.master_course_key_str))
'?master_course_id={}'.format(urllib.parse.quote_plus(self.master_course_key_str))
)
def test_authorization(self):
@@ -251,18 +250,18 @@ class CcxListTest(CcxRestApiTest):
resp = self.client.get(self.list_url, {}, HTTP_AUTHORIZATION=self.auth)
self.expect_error(status.HTTP_400_BAD_REQUEST, 'master_course_id_not_provided', resp)
base_url = six.moves.urllib.parse.urljoin(self.list_url, '?master_course_id=')
base_url = urllib.parse.urljoin(self.list_url, '?master_course_id=')
# case with empty master_course_id
resp = self.client.get(base_url, {}, HTTP_AUTHORIZATION=self.auth)
self.expect_error(status.HTTP_400_BAD_REQUEST, 'course_id_not_valid', resp)
# case with invalid master_course_id
url = '{0}invalid_master_course_str'.format(base_url)
url = f'{base_url}invalid_master_course_str'
resp = self.client.get(url, {}, HTTP_AUTHORIZATION=self.auth)
self.expect_error(status.HTTP_400_BAD_REQUEST, 'course_id_not_valid', resp)
# case with inexistent master_course_id
url = '{0}course-v1%3Aorg_foo.0%2Bcourse_bar_0%2BRun_0'.format(base_url)
url = f'{base_url}course-v1%3Aorg_foo.0%2Bcourse_bar_0%2BRun_0'
resp = self.client.get(url, {}, HTTP_AUTHORIZATION=self.auth)
self.expect_error(status.HTTP_404_NOT_FOUND, 'course_id_does_not_exist', resp)
@@ -298,13 +297,13 @@ class CcxListTest(CcxRestApiTest):
all_ccx = CustomCourseForEdX.objects.all()
all_ccx = all_ccx.order_by('id')
assert len(all_ccx) == num_ccx
title_str = u'Title CCX {0}'
title_str = 'Title CCX {0}'
for num, ccx in enumerate(all_ccx):
ccx.display_name = title_str.format(string.ascii_lowercase[-(num + 1)])
ccx.save()
# sort by display name
url = '{0}&order_by=display_name'.format(self.list_url_master_course)
url = f'{self.list_url_master_course}&order_by=display_name'
resp = self.client.get(url, {}, HTTP_AUTHORIZATION=self.auth)
assert resp.status_code == status.HTTP_200_OK
assert len(resp.data['results']) == num_ccx
@@ -313,7 +312,7 @@ class CcxListTest(CcxRestApiTest):
assert title_str.format(string.ascii_lowercase[(- (num_ccx - num))]) == ccx['display_name']
# add sort order desc
url = '{0}&order_by=display_name&sort_order=desc'.format(self.list_url_master_course)
url = f'{self.list_url_master_course}&order_by=display_name&sort_order=desc'
resp = self.client.get(url, {}, HTTP_AUTHORIZATION=self.auth)
# the only thing I can check is that the display name is in alphabetically reversed order
# in the same way when the field has been updated above, so with the id asc
@@ -341,7 +340,7 @@ class CcxListTest(CcxRestApiTest):
assert resp.data['previous'] is None
# get a page in the middle
url = '{0}&page=24'.format(self.list_url_master_course)
url = f'{self.list_url_master_course}&page=24'
resp = self.client.get(url, {}, HTTP_AUTHORIZATION=self.auth)
assert resp.status_code == status.HTTP_200_OK
assert resp.data['count'] == num_ccx
@@ -352,7 +351,7 @@ class CcxListTest(CcxRestApiTest):
assert resp.data['previous'] is not None
# get last page
url = '{0}&page={1}'.format(self.list_url_master_course, num_pages)
url = f'{self.list_url_master_course}&page={num_pages}'
resp = self.client.get(url, {}, HTTP_AUTHORIZATION=self.auth)
assert resp.status_code == status.HTTP_200_OK
assert resp.data['count'] == num_ccx
@@ -363,7 +362,7 @@ class CcxListTest(CcxRestApiTest):
assert resp.data['previous'] is not None
# last page + 1
url = '{0}&page={1}'.format(self.list_url_master_course, num_pages + 1)
url = '{}&page={}'.format(self.list_url_master_course, num_pages + 1)
resp = self.client.get(url, {}, HTTP_AUTHORIZATION=self.auth)
assert resp.status_code == status.HTTP_404_NOT_FOUND
@@ -583,13 +582,13 @@ class CcxListTest(CcxRestApiTest):
resp = self.client.post(self.list_url, data, format='json', HTTP_AUTHORIZATION=self.auth)
assert resp.status_code == status.HTTP_201_CREATED
# check if the response has at least the same data of the request
for key, val in six.iteritems(data):
for key, val in data.items():
assert resp.data.get(key) == val
assert 'ccx_course_id' in resp.data
# check that the new CCX actually exists
course_key = CourseKey.from_string(resp.data.get('ccx_course_id'))
ccx_course = CustomCourseForEdX.objects.get(pk=course_key.ccx)
assert six.text_type(CCXLocator.from_course_locator(ccx_course.course.id, ccx_course.id)) ==\
assert str(CCXLocator.from_course_locator(ccx_course.course.id, ccx_course.id)) ==\
resp.data.get('ccx_course_id')
# check that the coach user has coach role on the master course
coach_role_on_master_course = CourseCcxCoachRole(self.master_course_key)
@@ -692,12 +691,12 @@ class CcxDetailTest(CcxRestApiTest):
"""
Set up tests
"""
super(CcxDetailTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.make_coach()
# create a ccx
self.ccx = self.make_ccx(max_students_allowed=123)
self.ccx_key = CCXLocator.from_course_locator(self.ccx.course.id, self.ccx.id)
self.ccx_key_str = six.text_type(self.ccx_key)
self.ccx_key_str = str(self.ccx_key)
self.detail_url = reverse('ccx_api:v0:ccx:detail', kwargs={'ccx_course_id': self.ccx_key_str})
def make_ccx(self, max_students_allowed=200):
@@ -705,7 +704,7 @@ class CcxDetailTest(CcxRestApiTest):
Overridden method to replicate (part of) the actual
creation of ccx courses
"""
ccx = super(CcxDetailTest, self).make_ccx(max_students_allowed=max_students_allowed) # lint-amnesty, pylint: disable=super-with-arguments
ccx = super().make_ccx(max_students_allowed=max_students_allowed)
ccx.structure_json = json.dumps(self.master_course_chapters)
ccx.save()
@@ -824,7 +823,7 @@ class CcxDetailTest(CcxRestApiTest):
that only an URL with a valid course id string can reach the detail view.
"""
# get the base url from the valid one to build invalid urls
base_url = '{0}/'.format(self.detail_url.rsplit('/', 1)[0])
base_url = '{}/'.format(self.detail_url.rsplit('/', 1)[0])
# this url should be the same of the ccx list view
resolver = resolve(base_url)
assert views.CCXListView.__name__ == resolver.func.__name__
@@ -832,13 +831,13 @@ class CcxDetailTest(CcxRestApiTest):
# invalid urls
for invalid_ccx_id in ('foo', 'ccx-v1:org.0', 'ccx-v1:org.0+course_0'):
with pytest.raises(Resolver404):
resolve('{0}{1}'.format(base_url, invalid_ccx_id))
resolve(f'{base_url}{invalid_ccx_id}')
# the following course ID works even if it is not a CCX valid course id (the regex matches course ID strings)
resolver = resolve('{0}{1}'.format(base_url, 'ccx-v1:org.0+course_0+Run_0'))
resolver = resolve('{}{}'.format(base_url, 'ccx-v1:org.0+course_0+Run_0'))
assert views.CCXDetailView.__name__ == resolver.func.__name__
assert views.CCXDetailView.__module__ == resolver.func.__module__
# and of course a valid ccx course id
resolver = resolve('{0}{1}'.format(base_url, self.ccx_key_str))
resolver = resolve(f'{base_url}{self.ccx_key_str}')
assert views.CCXDetailView.__name__ == resolver.func.__name__
assert views.CCXDetailView.__module__ == resolver.func.__module__
@@ -880,7 +879,7 @@ class CcxDetailTest(CcxRestApiTest):
self.expect_error(status.HTTP_404_NOT_FOUND, 'ccx_course_id_does_not_exist', resp)
# get a valid ccx key and add few 0s to get a non existing ccx for a valid course
ccx_key_str = '{0}000000'.format(self.ccx_key_str)
ccx_key_str = f'{self.ccx_key_str}000000'
url = reverse('ccx_api:v0:ccx:detail', kwargs={'ccx_course_id': ccx_key_str})
# the permission class will give a 403 error because will not find the CCX
resp = client_request(url, {}, HTTP_AUTHORIZATION=self.auth)
@@ -902,8 +901,8 @@ class CcxDetailTest(CcxRestApiTest):
assert resp.data.get('display_name') == self.ccx.display_name
assert resp.data.get('max_students_allowed') == self.ccx.max_student_enrollments_allowed
assert resp.data.get('coach_email') == self.ccx.coach.email
assert resp.data.get('master_course_id') == six.text_type(self.ccx.course_id)
six.assertCountEqual(self, resp.data.get('course_modules'), self.master_course_chapters)
assert resp.data.get('master_course_id') == str(self.ccx.course_id)
assert len(resp.data.get('course_modules')) == len(self.master_course_chapters)
def test_delete_detail(self):
"""
@@ -1068,19 +1067,19 @@ class CcxDetailTest(CcxRestApiTest):
resp = self.client.patch(self.detail_url, data, format='json', HTTP_AUTHORIZATION=self.auth)
assert resp.status_code == status.HTTP_204_NO_CONTENT
ccx_from_db = CustomCourseForEdX.objects.get(id=self.ccx.id)
six.assertCountEqual(self, ccx_from_db.structure, data['course_modules'])
assert len(ccx_from_db.structure) == len(data['course_modules'])
data = {'course_modules': []}
resp = self.client.patch(self.detail_url, data, format='json', HTTP_AUTHORIZATION=self.auth)
assert resp.status_code == status.HTTP_204_NO_CONTENT
ccx_from_db = CustomCourseForEdX.objects.get(id=self.ccx.id)
six.assertCountEqual(self, ccx_from_db.structure, [])
assert len(ccx_from_db.structure) == len([])
data = {'course_modules': self.master_course_chapters}
resp = self.client.patch(self.detail_url, data, format='json', HTTP_AUTHORIZATION=self.auth)
assert resp.status_code == status.HTTP_204_NO_CONTENT
ccx_from_db = CustomCourseForEdX.objects.get(id=self.ccx.id)
six.assertCountEqual(self, ccx_from_db.structure, self.master_course_chapters)
assert len(ccx_from_db.structure) == len(self.master_course_chapters)
data = {'course_modules': None}
resp = self.client.patch(self.detail_url, data, format='json', HTTP_AUTHORIZATION=self.auth)
@@ -1093,7 +1092,7 @@ class CcxDetailTest(CcxRestApiTest):
resp = self.client.patch(self.detail_url, data, format='json', HTTP_AUTHORIZATION=self.auth)
assert resp.status_code == status.HTTP_204_NO_CONTENT
ccx_from_db = CustomCourseForEdX.objects.get(id=self.ccx.id)
six.assertCountEqual(self, ccx_from_db.structure, chapters)
assert len(ccx_from_db.structure) == len(chapters)
@ddt.data(
True,
@@ -1114,7 +1113,7 @@ class CcxDetailTest(CcxRestApiTest):
else:
assert resp.status_code == status.HTTP_204_NO_CONTENT
ccx_from_db = CustomCourseForEdX.objects.get(id=self.ccx.id)
six.assertCountEqual(self, ccx_from_db.structure, chapters)
assert len(ccx_from_db.structure) == len(chapters)
@ddt.data(
True,

View File

@@ -12,7 +12,7 @@ CCX_COURSE_ID_PATTERN = settings.COURSE_ID_PATTERN.replace('course_id', 'ccx_cou
CCX_URLS = ([
url(r'^$', views.CCXListView.as_view(), name='list'),
url(r'^{}/?$'.format(CCX_COURSE_ID_PATTERN), views.CCXDetailView.as_view(), name='detail'),
url(fr'^{CCX_COURSE_ID_PATTERN}/?$', views.CCXDetailView.as_view(), name='detail'),
], 'ccx')
app_name = 'v0'

View File

@@ -6,7 +6,6 @@ import json
import logging
import pytz
import six
from ccx_keys.locator import CCXLocator
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.db import transaction
@@ -20,15 +19,15 @@ from rest_framework.generics import GenericAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from lms.djangoapps.courseware import courses
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import CourseCcxCoachRole
from lms.djangoapps.ccx.models import CcxFieldOverride, CustomCourseForEdX
from lms.djangoapps.ccx.overrides import override_field_for_ccx
from lms.djangoapps.ccx.utils import add_master_course_staff_to_ccx, assign_staff_role_to_ccx, is_email
from lms.djangoapps.courseware import courses
from lms.djangoapps.instructor.enrollment import enroll_email, get_email_params
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.lib.api import authentication, permissions
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import CourseCcxCoachRole
from xmodule.modulestore.django import SignalHandler
from .paginators import CCXAPIPagination
@@ -66,14 +65,14 @@ def get_valid_course(course_id, is_ccx=False, advanced_course_check=False):
try:
course_key = CourseKey.from_string(course_id)
except InvalidKeyError:
log.info(u'Course ID string "%s" is not valid', course_id)
log.info('Course ID string "%s" is not valid', course_id)
return None, None, 'course_id_not_valid', status.HTTP_400_BAD_REQUEST
if not is_ccx:
try:
course_object = courses.get_course_by_id(course_key)
except Http404:
log.info(u'Master Course with ID "%s" not found', course_id)
log.info('Master Course with ID "%s" not found', course_id)
return None, None, 'course_id_does_not_exist', status.HTTP_404_NOT_FOUND
if advanced_course_check:
if course_object.id.deprecated:
@@ -85,7 +84,7 @@ def get_valid_course(course_id, is_ccx=False, advanced_course_check=False):
try:
ccx_id = course_key.ccx
except AttributeError:
log.info(u'Course ID string "%s" is not a valid CCX ID', course_id)
log.info('Course ID string "%s" is not a valid CCX ID', course_id)
return None, None, 'course_id_not_valid_ccx_id', status.HTTP_400_BAD_REQUEST
# get the master_course key
master_course_key = course_key.to_course_locator()
@@ -93,7 +92,7 @@ def get_valid_course(course_id, is_ccx=False, advanced_course_check=False):
ccx_course = CustomCourseForEdX.objects.get(id=ccx_id, course_id=master_course_key)
return ccx_course, course_key, None, None
except CustomCourseForEdX.DoesNotExist:
log.info(u'CCX Course with ID "%s" not found', course_id)
log.info('CCX Course with ID "%s" not found', course_id)
return None, None, 'ccx_course_id_does_not_exist', status.HTTP_404_NOT_FOUND
@@ -118,7 +117,7 @@ def get_valid_input(request_data, ignore_missing=False):
if not ignore_missing:
for field in mandatory_fields:
if field not in request_data:
field_errors[field] = {'error_code': 'missing_field_{0}'.format(field)}
field_errors[field] = {'error_code': f'missing_field_{field}'}
if field_errors:
return valid_input, field_errors
@@ -389,7 +388,7 @@ class CCXListView(GenericAPIView):
sort_direction = ''
if sort_order_input == 'desc':
sort_direction = '-'
queryset = queryset.order_by('{0}{1}'.format(sort_direction, order_by_input))
queryset = queryset.order_by(f'{sort_direction}{order_by_input}')
page = self.paginate_queryset(queryset)
serializer = self.get_serializer(page, many=True)
response = self.get_paginated_response(serializer.data)
@@ -492,7 +491,7 @@ class CCXListView(GenericAPIView):
# pull the ccx course key
ccx_course_key = CCXLocator.from_course_locator(
master_course_object.id,
six.text_type(ccx_course_object.id)
str(ccx_course_object.id)
)
# enroll the coach in the newly created ccx
email_params = get_email_params(
@@ -526,7 +525,7 @@ class CCXListView(GenericAPIView):
course_key=ccx_course_key
)
for rec, response in responses:
log.info(u'Signal fired when course is published. Receiver: %s. Response: %s', rec, response)
log.info('Signal fired when course is published. Receiver: %s. Response: %s', rec, response)
return Response(
status=status.HTTP_201_CREATED,
data=serializer.data
@@ -694,7 +693,7 @@ class CCXDetailView(GenericAPIView):
)
master_course_id = request.data.get('master_course_id')
if master_course_id is not None and six.text_type(ccx_course_object.course_id) != master_course_id:
if master_course_id is not None and str(ccx_course_object.course_id) != master_course_id:
return Response(
status=status.HTTP_403_FORBIDDEN,
data={
@@ -712,7 +711,7 @@ class CCXDetailView(GenericAPIView):
)
# get the master course key and master course object
master_course_object, master_course_key, _, _ = get_valid_course(six.text_type(ccx_course_object.course_id))
master_course_object, master_course_key, _, _ = get_valid_course(str(ccx_course_object.course_id))
with transaction.atomic():
# update the display name
@@ -780,7 +779,7 @@ class CCXDetailView(GenericAPIView):
course_key=ccx_course_key
)
for rec, response in responses:
log.info(u'Signal fired when course is published. Receiver: %s. Response: %s', rec, response)
log.info('Signal fired when course is published. Receiver: %s. Response: %s', rec, response)
return Response(
status=status.HTTP_204_NO_CONTENT,

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.conf import settings
from django.db import migrations, models
from opaque_keys.edx.django.models import CourseKeyField, UsageKeyField
@@ -19,7 +16,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('location', UsageKeyField(max_length=255, db_index=True)),
('field', models.CharField(max_length=255)),
('value', models.TextField(default=u'null')),
('value', models.TextField(default='null')),
],
),
migrations.CreateModel(
@@ -38,6 +35,6 @@ class Migration(migrations.Migration):
),
migrations.AlterUniqueTogether(
name='ccxfieldoverride',
unique_together=set([('ccx', 'location', 'field')]),
unique_together={('ccx', 'location', 'field')},
),
]

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models
@@ -14,6 +11,6 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='customcourseforedx',
name='structure_json',
field=models.TextField(null=True, verbose_name=u'Structure JSON', blank=True),
field=models.TextField(null=True, verbose_name='Structure JSON', blank=True),
),
]

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
import logging
import six
@@ -29,7 +26,7 @@ def add_master_course_staff_to_ccx_for_existing_ccx(apps, schema_editor):
if not ccx.course_id or ccx.course_id.deprecated:
# prevent migration for deprecated course ids or invalid ids.
continue
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, six.text_type(ccx.id))
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, str(ccx.id))
try:
course = get_course_by_id(ccx.course_id)
add_master_course_staff_to_ccx(
@@ -61,7 +58,7 @@ def remove_master_course_staff_from_ccx_for_existing_ccx(apps, schema_editor):
if not ccx.course_id or ccx.course_id.deprecated:
# prevent migration for deprecated course ids or invalid ids.
continue
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, six.text_type(ccx.id))
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, str(ccx.id))
try:
course = get_course_by_id(ccx.course_id)
remove_master_course_staff_from_ccx(

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
import logging
import six
@@ -52,7 +49,7 @@ def seed_forum_roles_for_existing_ccx(apps, schema_editor):
)
continue
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, six.text_type(ccx.id))
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, str(ccx.id))
# Create forum roles.
admin_role, _ = Role.objects.get_or_create(name=FORUM_ROLE_ADMINISTRATOR, course_id=ccx_locator)

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
import logging
import six
@@ -32,18 +29,18 @@ def change_existing_ccx_coaches_to_staff(apps, schema_editor):
return
list_ccx = CustomCourseForEdX.objects.using(db_alias).all()
for ccx in list_ccx:
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, six.text_type(ccx.id))
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, str(ccx.id))
try:
course = get_course_by_id(ccx_locator)
except Http404:
log.error('Could not migrate access for CCX course: %s', six.text_type(ccx_locator))
log.error('Could not migrate access for CCX course: %s', str(ccx_locator))
else:
coach = User.objects.get(id=ccx.coach.id)
allow_access(course, coach, 'staff', send_email=False)
revoke_access(course, coach, 'ccx_coach', send_email=False)
log.info(
'The CCX coach of CCX %s has been switched from "CCX Coach" to "Staff".',
six.text_type(ccx_locator)
str(ccx_locator)
)
def revert_ccx_staff_to_coaches(apps, schema_editor):
@@ -62,18 +59,18 @@ def revert_ccx_staff_to_coaches(apps, schema_editor):
return
list_ccx = CustomCourseForEdX.objects.using(db_alias).all()
for ccx in list_ccx:
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, six.text_type(ccx.id))
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, str(ccx.id))
try:
course = get_course_by_id(ccx_locator)
except Http404:
log.error('Could not migrate access for CCX course: %s', six.text_type(ccx_locator))
log.error('Could not migrate access for CCX course: %s', str(ccx_locator))
else:
coach = User.objects.get(id=ccx.coach.id)
allow_access(course, coach, 'ccx_coach', send_email=False)
revoke_access(course, coach, 'staff', send_email=False)
log.info(
'The CCX coach of CCX %s has been switched from "Staff" to "CCX Coach".',
six.text_type(ccx_locator)
str(ccx_locator)
)
class Migration(migrations.Migration):

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.15 on 2018-08-31 18:13

View File

@@ -7,7 +7,6 @@ import json
import logging
from datetime import datetime
import six
from ccx_keys.locator import CCXLocator
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.db import models
@@ -32,9 +31,9 @@ class CustomCourseForEdX(models.Model):
coach = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
# if not empty, this field contains a json serialized list of
# the master course modules
structure_json = models.TextField(verbose_name=u'Structure JSON', blank=True, null=True)
structure_json = models.TextField(verbose_name='Structure JSON', blank=True, null=True)
class Meta(object):
class Meta:
app_label = 'ccx'
@lazy
@@ -103,7 +102,7 @@ class CustomCourseForEdX(models.Model):
Returns:
The CCXLocator corresponding to this CCX.
"""
return CCXLocator.from_course_locator(self.course_id, six.text_type(self.id))
return CCXLocator.from_course_locator(self.course_id, str(self.id))
class CcxFieldOverride(models.Model):
@@ -116,8 +115,8 @@ class CcxFieldOverride(models.Model):
location = UsageKeyField(max_length=255, db_index=True)
field = models.CharField(max_length=255)
class Meta(object):
class Meta:
app_label = 'ccx'
unique_together = (('ccx', 'location', 'field'),)
value = models.TextField(default=u'null')
value = models.TextField(default='null')

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""A modulestore wrapper
It will 'unwrap' ccx keys on the way in and re-wrap them on the way out
@@ -14,7 +13,6 @@ version that was passed in.
from contextlib import contextmanager
from functools import partial
import six
from ccx_keys.locator import CCXBlockUsageLocator, CCXLocator
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
@@ -71,7 +69,7 @@ def restore_ccx_collection(field_value, ccx_id=None):
if isinstance(field_value, list):
field_value = [restore_ccx(fv, ccx_id) for fv in field_value]
elif isinstance(field_value, dict):
for key, val in six.iteritems(field_value):
for key, val in field_value.items():
field_value[key] = restore_ccx(val, ccx_id)
else:
field_value = restore_ccx(field_value, ccx_id)
@@ -88,7 +86,7 @@ def remove_ccx(to_strip):
yield stripped, partial(restore_ccx_collection, ccx_id=ccx)
class CCXModulestoreWrapper(object):
class CCXModulestoreWrapper:
"""This class wraps a modulestore
The purpose is to remove ccx-specific identifiers during lookup and restore

View File

@@ -40,7 +40,7 @@ class CustomCoursesForEdxOverrideProvider(FieldOverrideProvider):
elif hasattr(block, 'location'):
course_key = block.location.course_key
else:
msg = u"Unable to get course id when calculating ccx overide for block type %r"
msg = "Unable to get course id when calculating ccx overide for block type %r"
log.error(msg, type(block))
if course_key is not None:
ccx = get_current_ccx(course_key)

View File

@@ -3,6 +3,7 @@ Permission definitions for the ccx djangoapp
"""
from bridgekeeper import perms
from lms.djangoapps.courseware.rules import HasAccessRule
VIEW_CCX_COACH_DASHBOARD = 'ccx.view_ccx_coach_dashboard'

View File

@@ -5,7 +5,6 @@ Asynchronous tasks for the CCX app.
import logging
import six
from ccx_keys.locator import CCXLocator
from django.dispatch import receiver
from opaque_keys import InvalidKeyError
@@ -24,7 +23,7 @@ def course_published_handler(sender, course_key, **kwargs): # pylint: disable=u
Consume signals that indicate course published. If course already a CCX, do nothing.
"""
if not isinstance(course_key, CCXLocator):
send_ccx_course_published.delay(six.text_type(course_key))
send_ccx_course_published.delay(str(course_key))
@CELERY_APP.task
@@ -35,13 +34,13 @@ def send_ccx_course_published(course_key):
course_key = CourseLocator.from_string(course_key)
for ccx in CustomCourseForEdX.objects.filter(course_id=course_key):
try:
ccx_key = CCXLocator.from_course_locator(course_key, six.text_type(ccx.id))
ccx_key = CCXLocator.from_course_locator(course_key, str(ccx.id))
except InvalidKeyError:
log.info(u'Attempt to publish course with deprecated id. Course: %s. CCX: %s', course_key, ccx.id)
log.info('Attempt to publish course with deprecated id. Course: %s. CCX: %s', course_key, ccx.id)
continue
responses = SignalHandler.course_published.send(
sender=ccx,
course_key=ccx_key
)
for rec, response in responses:
log.info(u'Signal fired when course is published. Receiver: %s. Response: %s', rec, response)
log.info('Signal fired when course is published. Receiver: %s. Response: %s', rec, response)

View File

@@ -6,15 +6,15 @@ Dummy factories for tests
from factory import Sequence, SubFactory
from factory.django import DjangoModelFactory
from lms.djangoapps.ccx.models import CustomCourseForEdX
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.ccx.models import CustomCourseForEdX
# pylint: disable=missing-class-docstring
class CcxFactory(DjangoModelFactory):
class Meta(object):
class Meta:
model = CustomCourseForEdX
display_name = Sequence(lambda n: u'Test CCX #{0}'.format(n)) # pylint: disable=unnecessary-lambda
display_name = Sequence(lambda n: f'Test CCX #{n}') # pylint: disable=unnecessary-lambda
id = None # pylint: disable=invalid-name
coach = SubFactory(UserFactory)

View File

@@ -9,10 +9,10 @@ from itertools import chain
import pytz
from ccx_keys.locator import CCXLocator
from six.moves import range, zip_longest
from six.moves import zip_longest
from lms.djangoapps.ccx.models import CustomCourseForEdX
from common.djangoapps.student.tests.factories import AdminFactory, UserFactory
from lms.djangoapps.ccx.models import CustomCourseForEdX
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
@@ -24,7 +24,7 @@ class TestCCXModulestoreWrapper(SharedModuleStoreTestCase):
@classmethod
def setUpClass(cls):
super(TestCCXModulestoreWrapper, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
start = datetime.datetime(2010, 5, 12, 2, 42, tzinfo=pytz.UTC)
due = datetime.datetime(2010, 7, 7, 0, 0, tzinfo=pytz.UTC)
@@ -57,7 +57,7 @@ class TestCCXModulestoreWrapper(SharedModuleStoreTestCase):
"""
Set up tests
"""
super(TestCCXModulestoreWrapper, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.ccx = ccx = CustomCourseForEdX(
course_id=self.course.id,
display_name='Test CCX',

View File

@@ -1,4 +1,3 @@
# coding=UTF-8
"""
Performance tests for field overrides.
"""
@@ -6,12 +5,10 @@ Performance tests for field overrides.
import itertools
from datetime import datetime
from unittest import mock
import ddt
import mock
import pytest
import six
from six.moves import range
from ccx_keys.locator import CCXLocator
from django.conf import settings
from django.contrib.messages.storage.fallback import FallbackStorage
@@ -24,15 +21,15 @@ from opaque_keys.edx.keys import CourseKey
from pytz import UTC
from xblock.core import XBlock
from lms.djangoapps.courseware.testutils import FieldOverrideTestMixin
from lms.djangoapps.courseware.views.views import progress
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.ccx.tests.factories import CcxFactory
from lms.djangoapps.courseware.field_overrides import OverrideFieldData
from lms.djangoapps.courseware.testutils import FieldOverrideTestMixin
from lms.djangoapps.courseware.views.views import progress
from openedx.core.djangoapps.content.block_structure.api import get_course_in_cache
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import (
TEST_DATA_MONGO_MODULESTORE,
TEST_DATA_SPLIT_MODULESTORE,
@@ -67,7 +64,7 @@ class FieldOverridePerformanceTestCase(FieldOverrideTestMixin, ProceduralCourseT
"""
Create a test client, course, and user.
"""
super(FieldOverridePerformanceTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.request_factory = RequestFactory()
self.student = UserFactory.create()
@@ -140,7 +137,7 @@ class FieldOverridePerformanceTestCase(FieldOverrideTestMixin, ProceduralCourseT
self.student,
course_key
)
return CourseKey.from_string(six.text_type(course_key))
return CourseKey.from_string(str(course_key))
def grade_course(self, course_key):
"""
@@ -148,7 +145,7 @@ class FieldOverridePerformanceTestCase(FieldOverrideTestMixin, ProceduralCourseT
"""
return progress(
self.request,
course_id=six.text_type(course_key),
course_id=str(course_key),
student_id=self.student.id
)

View File

@@ -27,7 +27,7 @@ class TestCCX(ModuleStoreTestCase):
def setUp(self):
"""common setup for all tests"""
super(TestCCX, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create()
self.coach = AdminFactory.create()
role = CourseCcxCoachRole(self.course.id)

View File

@@ -1,26 +1,24 @@
# coding=UTF-8
"""
tests for overrides
"""
import datetime
from unittest import mock
import mock
import pytz
from ccx_keys.locator import CCXLocator
from django.test.utils import override_settings
from edx_django_utils.cache import RequestCache
from six.moves import range
from lms.djangoapps.courseware.courses import get_course_by_id
from lms.djangoapps.courseware.testutils import FieldOverrideTestMixin
from common.djangoapps.student.tests.factories import AdminFactory
from lms.djangoapps.ccx.models import CustomCourseForEdX
from lms.djangoapps.ccx.overrides import override_field_for_ccx
from lms.djangoapps.ccx.tests.utils import flatten, iter_blocks
from lms.djangoapps.courseware.courses import get_course_by_id
from lms.djangoapps.courseware.field_overrides import OverrideFieldData
from lms.djangoapps.courseware.tests.test_field_overrides import inject_field_overrides
from common.djangoapps.student.tests.factories import AdminFactory
from lms.djangoapps.courseware.testutils import FieldOverrideTestMixin
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
@@ -40,7 +38,7 @@ class TestFieldOverrides(FieldOverrideTestMixin, SharedModuleStoreTestCase):
"""
Course is created here and shared by all the class's tests.
"""
super(TestFieldOverrides, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
cls.course.enable_ccx = True
@@ -63,7 +61,7 @@ class TestFieldOverrides(FieldOverrideTestMixin, SharedModuleStoreTestCase):
"""
Set up tests
"""
super(TestFieldOverrides, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.ccx = ccx = CustomCourseForEdX(
course_id=self.course.id,

View File

@@ -4,16 +4,15 @@ Tests for celery tasks defined in tasks module
import contextlib
from unittest import mock
import mock
import six
from ccx_keys.locator import CCXLocator
from common.djangoapps.student.roles import CourseCcxCoachRole
from common.djangoapps.student.tests.factories import AdminFactory
from lms.djangoapps.ccx.tasks import send_ccx_course_published
from lms.djangoapps.ccx.tests.factories import CcxFactory
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from common.djangoapps.student.roles import CourseCcxCoachRole
from common.djangoapps.student.tests.factories import AdminFactory
from xmodule.modulestore.django import SignalHandler
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -39,7 +38,7 @@ class TestSendCCXCoursePublished(ModuleStoreTestCase):
"""
Set up tests
"""
super(TestSendCCXCoursePublished, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
course = self.course = CourseFactory.create(org="edX", course="999", display_name="Run 666")
course2 = self.course2 = CourseFactory.create(org="edX", course="999a", display_name="Run 667")
coach = AdminFactory.create()
@@ -54,7 +53,7 @@ class TestSendCCXCoursePublished(ModuleStoreTestCase):
"""
Call the function under test
"""
send_ccx_course_published(six.text_type(course_key))
send_ccx_course_published(str(course_key))
def test_signal_not_sent_for_ccx(self):
"""

View File

@@ -5,17 +5,17 @@ test utils
import uuid
from smtplib import SMTPException
from unittest import mock
import mock
from ccx_keys.locator import CCXLocator
from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentException
from common.djangoapps.student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole
from common.djangoapps.student.tests.factories import AdminFactory
from lms.djangoapps.ccx.tests.factories import CcxFactory
from lms.djangoapps.ccx.tests.utils import CcxTestCase
from lms.djangoapps.ccx.utils import add_master_course_staff_to_ccx, ccx_course, remove_master_course_staff_from_ccx
from lms.djangoapps.instructor.access import list_with_level
from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentException
from common.djangoapps.student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole
from common.djangoapps.student.tests.factories import AdminFactory
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -27,7 +27,7 @@ class TestGetCCXFromCCXLocator(ModuleStoreTestCase):
def setUp(self):
"""Set up a course, coach, ccx and user"""
super(TestGetCCXFromCCXLocator, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create()
coach = self.coach = AdminFactory.create()
role = CourseCcxCoachRole(self.course.id)
@@ -60,7 +60,7 @@ class TestStaffOnCCX(CcxTestCase):
MODULESTORE = TEST_DATA_SPLIT_MODULESTORE
def setUp(self):
super(TestStaffOnCCX, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Create instructor account
self.client.login(username=self.coach.username, password="test")
@@ -237,7 +237,7 @@ class TestStaffOnCCX(CcxTestCase):
assert CourseInstructorRole(self.course.id).has_user(instructor)
outbox = self.get_outbox()
# create a unique display name
display_name = 'custom_display_{}'.format(uuid.uuid4())
display_name = f'custom_display_{uuid.uuid4()}'
list_staff_master_course = list_with_level(self.course, 'staff')
list_instructor_master_course = list_with_level(self.course, 'instructor')
assert len(outbox) == 0
@@ -262,7 +262,7 @@ class TestStaffOnCCX(CcxTestCase):
outbox = self.get_outbox()
add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False)
# create a unique display name
display_name = 'custom_display_{}'.format(uuid.uuid4())
display_name = f'custom_display_{uuid.uuid4()}'
list_staff_master_course = list_with_level(self.course, 'staff')
list_instructor_master_course = list_with_level(self.course, 'instructor')
assert len(outbox) == 0

View File

@@ -6,10 +6,10 @@ test views
import datetime
import json
import re
from unittest.mock import MagicMock, patch
import ddt
import six
from six.moves import range, zip
from ccx_keys.locator import CCXLocator
from django.conf import settings
from django.test import RequestFactory
@@ -17,32 +17,31 @@ from django.test.utils import override_settings
from django.urls import resolve, reverse
from django.utils.translation import ugettext as _
from edx_django_utils.cache import RequestCache
from mock import MagicMock, patch
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
from capa.tests.response_xml_factory import StringResponseXMLFactory
from lms.djangoapps.courseware.courses import get_course_by_id
from lms.djangoapps.courseware.tabs import get_course_tab_list
from lms.djangoapps.courseware.tests.factories import StudentModuleFactory
from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase
from lms.djangoapps.courseware.testutils import FieldOverrideTestMixin
from common.djangoapps.edxmako.shortcuts import render_to_response
from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentAllowed
from common.djangoapps.student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole
from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
from lms.djangoapps.ccx.models import CustomCourseForEdX
from lms.djangoapps.ccx.overrides import get_override_for_ccx, override_field_for_ccx
from lms.djangoapps.ccx.tests.factories import CcxFactory
from lms.djangoapps.ccx.tests.utils import CcxTestCase, flatten
from lms.djangoapps.ccx.utils import ccx_course, is_email
from lms.djangoapps.ccx.views import get_date
from lms.djangoapps.courseware.courses import get_course_by_id
from lms.djangoapps.courseware.tabs import get_course_tab_list
from lms.djangoapps.courseware.tests.factories import StudentModuleFactory
from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase
from lms.djangoapps.courseware.testutils import FieldOverrideTestMixin
from lms.djangoapps.discussion.django_comment_client.utils import has_forum_access
from lms.djangoapps.grades.api import task_compute_all_grades_for_course
from lms.djangoapps.instructor.access import allow_access, list_with_level
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.django_comment_common.models import FORUM_ROLE_ADMINISTRATOR
from openedx.core.djangoapps.django_comment_common.utils import are_permissions_roles_seeded
from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentAllowed
from common.djangoapps.student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole
from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import (
@@ -88,7 +87,7 @@ def setup_students_and_grades(context):
context.student = student = UserFactory.create()
CourseEnrollmentFactory.create(user=student, course_id=context.course.id)
context.student2 = student2 = UserFactory.create(username=u'u\u0131\u028c\u0279\u0250\u026f')
context.student2 = student2 = UserFactory.create(username='u\u0131\u028c\u0279\u0250\u026f')
CourseEnrollmentFactory.create(user=student2, course_id=context.course.id)
# create grades for self.student as if they'd submitted the ccx
@@ -112,7 +111,7 @@ def setup_students_and_grades(context):
module_state_key=problem.location
)
task_compute_all_grades_for_course.apply_async(kwargs={'course_key': six.text_type(context.course.id)})
task_compute_all_grades_for_course.apply_async(kwargs={'course_key': str(context.course.id)})
def unhide(unit):
@@ -132,7 +131,7 @@ class TestAdminAccessCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
MODULESTORE = TEST_DATA_SPLIT_MODULESTORE
def setUp(self):
super(TestAdminAccessCoachDashboard, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.make_coach()
ccx = self.make_ccx()
ccx_key = CCXLocator.from_course_locator(self.course.id, ccx.id)
@@ -182,24 +181,24 @@ class TestCCXProgressChanges(CcxTestCase, LoginEnrollmentTestCase):
"""
Set up tests
"""
super(TestCCXProgressChanges, cls).setUpClass()
super().setUpClass()
start = datetime.datetime(2016, 7, 1, 0, 0, tzinfo=UTC)
due = datetime.datetime(2016, 7, 8, 0, 0, tzinfo=UTC)
cls.course = course = CourseFactory.create(enable_ccx=True, start=start)
chapter = ItemFactory.create(start=start, parent=course, category=u'chapter')
chapter = ItemFactory.create(start=start, parent=course, category='chapter')
sequential = ItemFactory.create(
parent=chapter,
start=start,
due=due,
category=u'sequential',
category='sequential',
metadata={'graded': True, 'format': 'Homework'}
)
vertical = ItemFactory.create(
parent=sequential,
start=start,
due=due,
category=u'vertical',
category='vertical',
metadata={'graded': True, 'format': 'Homework'}
)
@@ -231,7 +230,7 @@ class TestCCXProgressChanges(CcxTestCase, LoginEnrollmentTestCase):
grade_summary = progress_page_response.mako_context['courseware_summary']
chapter = grade_summary[0]
section = chapter['sections'][0]
progress_page_due_date = section.due.strftime(u"%Y-%m-%d %H:%M")
progress_page_due_date = section.due.strftime("%Y-%m-%d %H:%M")
assert progress_page_due_date == due
@patch('lms.djangoapps.ccx.views.render_to_response', intercept_renderer)
@@ -243,7 +242,7 @@ class TestCCXProgressChanges(CcxTestCase, LoginEnrollmentTestCase):
"""
self.make_coach()
ccx = self.make_ccx()
ccx_course_key = CCXLocator.from_course_locator(self.course.id, six.text_type(ccx.id))
ccx_course_key = CCXLocator.from_course_locator(self.course.id, str(ccx.id))
self.client.login(username=self.coach.username, password="test")
url = reverse('ccx_coach_dashboard', kwargs={'course_id': ccx_course_key})
@@ -256,8 +255,8 @@ class TestCCXProgressChanges(CcxTestCase, LoginEnrollmentTestCase):
# edit schedule
date = datetime.datetime.now() - datetime.timedelta(days=5)
start = date.strftime(u"%Y-%m-%d %H:%M")
due = (date + datetime.timedelta(days=3)).strftime(u"%Y-%m-%d %H:%M")
start = date.strftime("%Y-%m-%d %H:%M")
due = (date + datetime.timedelta(days=3)).strftime("%Y-%m-%d %H:%M")
schedule[0]['start'] = start
schedule[0]['children'][0]['start'] = start
@@ -293,7 +292,7 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
@classmethod
def setUpClass(cls):
super(TestCoachDashboard, cls).setUpClass()
super().setUpClass()
cls.course_disable_ccx = CourseFactory.create(enable_ccx=False)
cls.course_with_ccx_connect_set = CourseFactory.create(enable_ccx=True, ccx_connector="http://ccx.com")
@@ -301,7 +300,7 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
"""
Set up tests
"""
super(TestCoachDashboard, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Login with the instructor account
self.client.login(username=self.coach.username, password="test")
@@ -338,7 +337,7 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
self.make_coach()
url = reverse(
'ccx_coach_dashboard',
kwargs={'course_id': six.text_type(self.course.id)})
kwargs={'course_id': str(self.course.id)})
response = self.client.get(url)
assert response.status_code == 200
assert re.search('<form action=".+create_ccx"', response.content.decode('utf-8'))
@@ -352,7 +351,7 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
url = reverse(
'create_ccx',
kwargs={'course_id': six.text_type(self.course_with_ccx_connect_set.id)})
kwargs={'course_id': str(self.course_with_ccx_connect_set.id)})
response = self.client.get(url)
assert response.status_code == 200
@@ -371,7 +370,7 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
self.make_coach()
url = reverse(
'create_ccx',
kwargs={'course_id': six.text_type(self.course.id)})
kwargs={'course_id': str(self.course.id)})
response = self.client.post(url, {'name': ccx_name})
assert response.status_code == 302
@@ -485,11 +484,11 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
assert len(schedule) == 2
assert schedule[0]['hidden'] is False
# If a coach does not override dates, then dates will be imported from master course.
assert schedule[0]['start'] == self.chapters[0].start.strftime(u'%Y-%m-%d %H:%M')
assert schedule[0]['children'][0]['start'] == self.sequentials[0].start.strftime(u'%Y-%m-%d %H:%M')
assert schedule[0]['start'] == self.chapters[0].start.strftime('%Y-%m-%d %H:%M')
assert schedule[0]['children'][0]['start'] == self.sequentials[0].start.strftime('%Y-%m-%d %H:%M')
if self.sequentials[0].due:
expected_due = self.sequentials[0].due.strftime(u'%Y-%m-%d %H:%M')
expected_due = self.sequentials[0].due.strftime('%Y-%m-%d %H:%M')
else:
expected_due = None
assert schedule[0]['children'][0]['due'] == expected_due
@@ -499,10 +498,10 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
kwargs={'course_id': CCXLocator.from_course_locator(self.course.id, ccx.id)})
unhide(schedule[0])
schedule[0]['start'] = u'2014-11-20 00:00'
schedule[0]['children'][0]['due'] = u'2014-12-25 00:00' # what a jerk!
schedule[0]['children'][0]['children'][0]['start'] = u'2014-12-20 00:00'
schedule[0]['children'][0]['children'][0]['due'] = u'2014-12-25 00:00'
schedule[0]['start'] = '2014-11-20 00:00'
schedule[0]['children'][0]['due'] = '2014-12-25 00:00' # what a jerk!
schedule[0]['children'][0]['children'][0]['start'] = '2014-12-20 00:00'
schedule[0]['children'][0]['children'][0]['due'] = '2014-12-25 00:00'
response = self.client.post(
url, json.dumps(schedule), content_type='application/json'
@@ -510,17 +509,17 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
schedule = json.loads(response.content.decode('utf-8'))['schedule']
assert schedule[0]['hidden'] is False
assert schedule[0]['start'] == u'2014-11-20 00:00'
assert schedule[0]['children'][0]['due'] == u'2014-12-25 00:00'
assert schedule[0]['start'] == '2014-11-20 00:00'
assert schedule[0]['children'][0]['due'] == '2014-12-25 00:00'
assert schedule[0]['children'][0]['children'][0]['due'] == u'2014-12-25 00:00'
assert schedule[0]['children'][0]['children'][0]['start'] == u'2014-12-20 00:00'
assert schedule[0]['children'][0]['children'][0]['due'] == '2014-12-25 00:00'
assert schedule[0]['children'][0]['children'][0]['start'] == '2014-12-20 00:00'
# Make sure start date set on course, follows start date of earliest
# scheduled chapter
ccx = CustomCourseForEdX.objects.get()
course_start = get_override_for_ccx(ccx, self.course, 'start')
assert str(course_start)[:(- 9)] == self.chapters[0].start.strftime(u'%Y-%m-%d %H:%M')
assert str(course_start)[:(- 9)] == self.chapters[0].start.strftime('%Y-%m-%d %H:%M')
# Make sure grading policy adjusted
policy = get_override_for_ccx(ccx, self.course, 'grading_policy',
@@ -614,7 +613,7 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
)
data = {
button_tuple[0]: button_tuple[1],
student_form_input_name: u','.join([student.email, ]),
student_form_input_name: ','.join([student.email, ]),
}
if send_email:
data['email-students'] = 'Notify-students-by-email'
@@ -654,7 +653,7 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
)
data = {
'enrollment-button': 'Enroll',
'student-ids': u','.join([student.email for student in students]),
'student-ids': ','.join([student.email for student in students]),
}
response = self.client.post(url, data=data, follow=True)
assert response.status_code == 200
@@ -696,7 +695,7 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
)
data = {
button_tuple[0]: button_tuple[1],
student_form_input_name: u','.join([student.email, ]),
student_form_input_name: ','.join([student.email, ]),
}
if send_email:
data['email-students'] = 'Notify-students-by-email'
@@ -738,7 +737,7 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
)
data = {
button_tuple[0]: button_tuple[1],
student_form_input_name: u','.join([identifier, ]),
student_form_input_name: ','.join([identifier, ]),
}
if send_email:
data['email-students'] = 'Notify-students-by-email'
@@ -786,7 +785,7 @@ class TestCoachDashboard(CcxTestCase, LoginEnrollmentTestCase):
)
data = {
button_tuple[0]: button_tuple[1],
student_form_input_name: u','.join([identifier, ]),
student_form_input_name: ','.join([identifier, ]),
}
if send_email:
data['email-students'] = 'Notify-students-by-email'
@@ -807,7 +806,7 @@ class TestCoachDashboardSchedule(CcxTestCase, LoginEnrollmentTestCase, ModuleSto
ENABLED_CACHES = ['default', 'mongo_inheritance_cache', 'loc_cache']
def setUp(self):
super(TestCoachDashboardSchedule, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = course = CourseFactory.create(enable_ccx=True)
# Create a course outline
@@ -829,7 +828,7 @@ class TestCoachDashboardSchedule(CcxTestCase, LoginEnrollmentTestCase, ModuleSto
self.verticals = flatten([
[
ItemFactory.create(
start=start, due=due, parent=sequential, graded=True, format='Homework', category=u'vertical'
start=start, due=due, parent=sequential, graded=True, format='Homework', category='vertical'
) for _ in range(2)
] for sequential in self.sequentials
])
@@ -913,17 +912,17 @@ class TestCoachDashboardSchedule(CcxTestCase, LoginEnrollmentTestCase, ModuleSto
vertical = self.verticals[0]
self.hide_node(vertical)
locations = self.assert_elements_in_schedule(url, n_verticals=7)
assert six.text_type(vertical.location) not in locations
assert str(vertical.location) not in locations
# hide a sequential
sequential = self.sequentials[0]
self.hide_node(sequential)
locations = self.assert_elements_in_schedule(url, n_sequentials=3, n_verticals=6)
assert six.text_type(sequential.location) not in locations
assert str(sequential.location) not in locations
# hide a chapter
chapter = self.chapters[0]
self.hide_node(chapter)
locations = self.assert_elements_in_schedule(url, n_chapters=1, n_sequentials=2, n_verticals=4)
assert six.text_type(chapter.location) not in locations
assert str(chapter.location) not in locations
GET_CHILDREN = XModuleMixin.get_children
@@ -953,7 +952,7 @@ class TestCCXGrades(FieldOverrideTestMixin, SharedModuleStoreTestCase, LoginEnro
@classmethod
def setUpClass(cls):
super(TestCCXGrades, cls).setUpClass()
super().setUpClass()
cls._course = course = CourseFactory.create(enable_ccx=True)
CourseOverview.load_from_module_store(course.id)
@@ -987,7 +986,7 @@ class TestCCXGrades(FieldOverrideTestMixin, SharedModuleStoreTestCase, LoginEnro
"""
Set up tests
"""
super(TestCCXGrades, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Create instructor account
self.coach = coach = AdminFactory.create()
@@ -1015,7 +1014,7 @@ class TestCCXGrades(FieldOverrideTestMixin, SharedModuleStoreTestCase, LoginEnro
# create a ccx locator and retrieve the course structure using that key
# which emulates how a student would get access.
self.ccx_key = CCXLocator.from_course_locator(self._course.id, six.text_type(ccx.id))
self.ccx_key = CCXLocator.from_course_locator(self._course.id, str(ccx.id))
self.course = get_course_by_id(self.ccx_key, depth=None)
CourseOverview.load_from_module_store(self.course.id)
setup_students_and_grades(self)
@@ -1098,12 +1097,12 @@ class CCXCoachTabTestCase(CcxTestCase):
"""
@classmethod
def setUpClass(cls):
super(CCXCoachTabTestCase, cls).setUpClass()
super().setUpClass()
cls.ccx_enabled_course = CourseFactory.create(enable_ccx=True)
cls.ccx_disabled_course = CourseFactory.create(enable_ccx=False)
def setUp(self):
super(CCXCoachTabTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.user = UserFactory.create()
for course in [self.ccx_enabled_course, self.ccx_disabled_course]:
CourseEnrollmentFactory.create(user=self.user, course_id=course.id)
@@ -1153,7 +1152,7 @@ class CCXCoachTabTestCase(CcxTestCase):
"""
self.make_coach()
ccx = self.make_ccx()
ccx_key = CCXLocator.from_course_locator(self.course.id, six.text_type(ccx.id))
ccx_key = CCXLocator.from_course_locator(self.course.id, str(ccx.id))
staff = self.make_staff()
with ccx_course(ccx_key) as course_ccx:
@@ -1182,7 +1181,7 @@ class CCXCoachTabTestCase(CcxTestCase):
"""
self.make_coach()
ccx = self.make_ccx()
ccx_key = CCXLocator.from_course_locator(self.course.id, six.text_type(ccx.id))
ccx_key = CCXLocator.from_course_locator(self.course.id, str(ccx.id))
instructor = self.make_instructor()
with ccx_course(ccx_key) as course_ccx:
@@ -1200,7 +1199,7 @@ class TestStudentViewsWithCCX(ModuleStoreTestCase):
"""
Set up courses and enrollments.
"""
super(TestStudentViewsWithCCX, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Create a Draft Mongo and a Split Mongo course and enroll a student user in them.
self.student_password = "foobar"
@@ -1230,5 +1229,5 @@ class TestStudentViewsWithCCX(ModuleStoreTestCase):
def test_load_courseware(self):
self.client.login(username=self.student.username, password=self.student_password)
response = self.client.get(reverse('courseware', kwargs={'course_id': six.text_type(self.ccx_course_key)}))
response = self.client.get(reverse('courseware', kwargs={'course_id': str(self.ccx_course_key)}))
assert response.status_code == 200

View File

@@ -7,13 +7,12 @@ import datetime
import pytz
from django.conf import settings
from six.moves import range
from common.djangoapps.student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.ccx.overrides import override_field_for_ccx
from lms.djangoapps.ccx.tests.factories import CcxFactory
from openedx.core.djangoapps.ace_common.tests.mixins import EmailTemplateTagMixin
from common.djangoapps.student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole
from common.djangoapps.student.tests.factories import UserFactory
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
@@ -29,7 +28,7 @@ class CcxTestCase(EmailTemplateTagMixin, SharedModuleStoreTestCase):
@classmethod
def setUpClass(cls):
super(CcxTestCase, cls).setUpClass()
super().setUpClass()
cls.course = course = CourseFactory.create(enable_ccx=True)
# Create a course outline
@@ -51,7 +50,7 @@ class CcxTestCase(EmailTemplateTagMixin, SharedModuleStoreTestCase):
cls.verticals = flatten([
[
ItemFactory.create(
start=start, due=due, parent=sequential, graded=True, format='Homework', category=u'vertical'
start=start, due=due, parent=sequential, graded=True, format='Homework', category='vertical'
) for _ in range(2)
] for sequential in cls.sequentials
])
@@ -69,7 +68,7 @@ class CcxTestCase(EmailTemplateTagMixin, SharedModuleStoreTestCase):
"""
Set up tests
"""
super(CcxTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Create instructor account
self.coach = UserFactory.create(password="test")
# create an instance of modulestore
@@ -133,6 +132,5 @@ def iter_blocks(course):
""" get child blocks """
yield block
for child in block.get_children():
for descendant in visit(child): # wish they'd backport yield from
yield descendant
yield from visit(child)
return visit(course)

View File

@@ -16,19 +16,18 @@ from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from django.urls import reverse
from django.utils.translation import ugettext as _
from six.moves import map
from lms.djangoapps.courseware.courses import get_course_by_id
from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentException
from common.djangoapps.student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole
from lms.djangoapps.ccx.custom_exception import CCXUserValidationException
from lms.djangoapps.ccx.models import CustomCourseForEdX
from lms.djangoapps.ccx.overrides import get_override_for_ccx
from lms.djangoapps.courseware.courses import get_course_by_id
from lms.djangoapps.instructor.access import allow_access, list_with_level, revoke_access
from lms.djangoapps.instructor.enrollment import enroll_email, get_email_params, unenroll_email
from lms.djangoapps.instructor.views.api import _split_input_list
from lms.djangoapps.instructor.views.tools import get_student_from_identifier
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from common.djangoapps.student.models import CourseEnrollment, CourseEnrollmentException
from common.djangoapps.student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole
log = logging.getLogger("edx.ccx")
@@ -63,7 +62,7 @@ def get_ccx_from_ccx_locator(course_id):
ccx = CustomCourseForEdX.objects.filter(id=ccx_id)
if not ccx:
log.warning(
u"CCX does not exist for course with id %s",
"CCX does not exist for course with id %s",
course_id
)
return None
@@ -88,10 +87,10 @@ def get_date(ccx, node, date_type=None, parent_node=None):
if date is not None:
# Setting override date [start or due]
date = date.strftime(u'%Y-%m-%d %H:%M')
date = date.strftime('%Y-%m-%d %H:%M')
elif not parent_node and master_date is not None:
# Setting date from master course
date = master_date.strftime(u'%Y-%m-%d %H:%M')
date = master_date.strftime('%Y-%m-%d %H:%M')
elif parent_node is not None:
# Set parent date (vertical has same dates as subsections)
date = get_date(ccx, node=parent_node, date_type=date_type)
@@ -226,7 +225,7 @@ def get_valid_student_with_email(identifier):
try:
validate_email(email)
except ValidationError:
raise CCXUserValidationException(u'Could not find a user with name or email "{0}" '.format(identifier)) # lint-amnesty, pylint: disable=raise-missing-from
raise CCXUserValidationException(f'Could not find a user with name or email "{identifier}" ') # lint-amnesty, pylint: disable=raise-missing-from
return email, user
@@ -260,14 +259,14 @@ def ccx_students_enrolling_center(action, identifiers, email_students, course_ke
if student:
must_enroll = student in staff or student in admins or student == coach
except CCXUserValidationException as exp:
log.info(u"%s", exp)
errors.append(u"{0}".format(exp))
log.info("%s", exp)
errors.append(f"{exp}")
continue
if CourseEnrollment.objects.is_course_full(ccx_course_overview) and not must_enroll:
error = _(u'The course is full: the limit is {max_student_enrollments_allowed}').format(
error = _('The course is full: the limit is {max_student_enrollments_allowed}').format(
max_student_enrollments_allowed=ccx_course_overview.max_student_enrollments_allowed)
log.info(u"%s", error)
log.info("%s", error)
errors.append(error)
break
enroll_email(course_key, email, auto_enroll=True, email_students=email_students, email_params=email_params)
@@ -276,8 +275,8 @@ def ccx_students_enrolling_center(action, identifiers, email_students, course_ke
try:
email, __ = get_valid_student_with_email(identifier)
except CCXUserValidationException as exp:
log.info(u"%s", exp)
errors.append(u"{0}".format(exp))
log.info("%s", exp)
errors.append(f"{exp}")
continue
unenroll_email(course_key, email, email_students=email_students, email_params=email_params)
return errors
@@ -357,7 +356,7 @@ def add_master_course_staff_to_ccx(master_course, ccx_key, display_name, send_em
allow_access(course_ccx, staff, 'staff')
except CourseEnrollmentException:
log.warning(
u"Unable to enroll staff %s to course with id %s",
"Unable to enroll staff %s to course with id %s",
staff.email,
ccx_key
)
@@ -382,7 +381,7 @@ def add_master_course_staff_to_ccx(master_course, ccx_key, display_name, send_em
allow_access(course_ccx, instructor, 'instructor')
except CourseEnrollmentException:
log.warning(
u"Unable to enroll instructor %s to course with id %s",
"Unable to enroll instructor %s to course with id %s",
instructor.email,
ccx_key
)

View File

@@ -9,7 +9,6 @@ import functools
import json
import logging
from copy import deepcopy
from six import StringIO
import pytz
import six
@@ -25,9 +24,11 @@ from django.utils.translation import ugettext as _
from django.views.decorators.cache import cache_control
from django.views.decorators.csrf import ensure_csrf_cookie
from opaque_keys.edx.keys import CourseKey
from six import StringIO
from lms.djangoapps.courseware.courses import get_course_by_id
from common.djangoapps.edxmako.shortcuts import render_to_response
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import CourseCcxCoachRole
from lms.djangoapps.ccx.models import CustomCourseForEdX
from lms.djangoapps.ccx.overrides import (
bulk_delete_ccx_override_fields,
@@ -48,14 +49,13 @@ from lms.djangoapps.ccx.utils import (
get_enrollment_action_and_identifiers,
parse_date
)
from lms.djangoapps.courseware.courses import get_course_by_id
from lms.djangoapps.courseware.field_overrides import disable_overrides
from lms.djangoapps.grades.api import CourseGradeFactory
from lms.djangoapps.instructor.enrollment import enroll_email, get_email_params
from lms.djangoapps.instructor.views.gradebook_api import get_grade_book_page
from openedx.core.djangoapps.django_comment_common.models import FORUM_ROLE_ADMINISTRATOR, assign_role
from openedx.core.djangoapps.django_comment_common.utils import seed_permissions_roles
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import CourseCcxCoachRole
from xmodule.modulestore.django import SignalHandler
log = logging.getLogger(__name__)
@@ -123,7 +123,7 @@ def dashboard(request, course, ccx=None):
if ccx:
url = reverse(
'ccx_coach_dashboard',
kwargs={'course_id': CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))}
kwargs={'course_id': CCXLocator.from_course_locator(course.id, str(ccx.id))}
)
return redirect(url)
@@ -134,7 +134,7 @@ def dashboard(request, course, ccx=None):
context.update(get_ccx_creation_dict(course))
if ccx:
ccx_locator = CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))
ccx_locator = CCXLocator.from_course_locator(course.id, str(ccx.id))
# At this point we are done with verification that current user is ccx coach.
assign_staff_role_to_ccx(ccx_locator, request.user, course.id)
schedule = get_ccx_schedule(course, ccx)
@@ -211,7 +211,7 @@ def create_ccx(request, course, ccx=None):
for vertical in sequential.get_children():
override_field_for_ccx(ccx, vertical, hidden, True)
ccx_id = CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))
ccx_id = CCXLocator.from_course_locator(course.id, str(ccx.id))
# Create forum roles
seed_permissions_roles(ccx_id)
@@ -236,10 +236,10 @@ def create_ccx(request, course, ccx=None):
# using CCX object as sender here.
responses = SignalHandler.course_published.send(
sender=ccx,
course_key=CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))
course_key=CCXLocator.from_course_locator(course.id, str(ccx.id))
)
for rec, response in responses:
log.info(u'Signal fired when course is published. Receiver: %s. Response: %s', rec, response)
log.info('Signal fired when course is published. Receiver: %s. Response: %s', rec, response)
return redirect(url)
@@ -298,7 +298,7 @@ def save_ccx(request, course, ccx=None): # lint-amnesty, pylint: disable=too-ma
children = unit.get('children', None)
# For a vertical, override start and due dates of all its problems.
if unit.get('category', None) == u'vertical':
if unit.get('category', None) == 'vertical':
for component in block.get_children():
# override start and due date of problem (Copy dates of vertical into problems)
if start:
@@ -335,10 +335,10 @@ def save_ccx(request, course, ccx=None): # lint-amnesty, pylint: disable=too-ma
# using CCX object as sender here.
responses = SignalHandler.course_published.send(
sender=ccx,
course_key=CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))
course_key=CCXLocator.from_course_locator(course.id, str(ccx.id))
)
for rec, response in responses:
log.info(u'Signal fired when course is published. Receiver: %s. Response: %s', rec, response)
log.info('Signal fired when course is published. Receiver: %s. Response: %s', rec, response)
return HttpResponse( # lint-amnesty, pylint: disable=http-response-with-content-type-json, http-response-with-json-dumps
json.dumps({
@@ -364,14 +364,14 @@ def set_grading_policy(request, course, ccx=None):
# using CCX object as sender here.
responses = SignalHandler.course_published.send(
sender=ccx,
course_key=CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))
course_key=CCXLocator.from_course_locator(course.id, str(ccx.id))
)
for rec, response in responses:
log.info(u'Signal fired when course is published. Receiver: %s. Response: %s', rec, response)
log.info('Signal fired when course is published. Receiver: %s. Response: %s', rec, response)
url = reverse(
'ccx_coach_dashboard',
kwargs={'course_id': CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))}
kwargs={'course_id': CCXLocator.from_course_locator(course.id, str(ccx.id))}
)
return redirect(url)
@@ -470,7 +470,7 @@ def ccx_students_management(request, course, ccx=None):
action, identifiers = get_enrollment_action_and_identifiers(request)
email_students = 'email-students' in request.POST
course_key = CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))
course_key = CCXLocator.from_course_locator(course.id, str(ccx.id))
email_params = get_email_params(course, auto_enroll=True, course_key=course_key, display_name=ccx.display_name)
errors = ccx_students_enrolling_center(action, identifiers, email_students, course_key, email_params, ccx.coach)
@@ -493,7 +493,7 @@ def ccx_gradebook(request, course, ccx=None):
if not ccx:
raise Http404
ccx_key = CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))
ccx_key = CCXLocator.from_course_locator(course.id, str(ccx.id))
with ccx_course(ccx_key) as course: # lint-amnesty, pylint: disable=redefined-argument-from-local
student_info, page = get_grade_book_page(request, course, course_key=ccx_key)
@@ -520,7 +520,7 @@ def ccx_grades_csv(request, course, ccx=None):
if not ccx:
raise Http404
ccx_key = CCXLocator.from_course_locator(course.id, six.text_type(ccx.id))
ccx_key = CCXLocator.from_course_locator(course.id, str(ccx.id))
with ccx_course(ccx_key) as course: # lint-amnesty, pylint: disable=redefined-argument-from-local
enrolled_students = User.objects.filter(
@@ -539,12 +539,12 @@ def ccx_grades_csv(request, course, ccx=None):
# Encode the header row in utf-8 encoding in case there are
# unicode characters
header = [section['label'].encode('utf-8') if six.PY2 else section['label']
for section in course_grade.summary[u'section_breakdown']]
for section in course_grade.summary['section_breakdown']]
rows.append(["id", "email", "username", "grade"] + header)
percents = {
section['label']: section.get('percent', 0.0)
for section in course_grade.summary[u'section_breakdown']
for section in course_grade.summary['section_breakdown']
if 'label' in section
}