diff --git a/cms/djangoapps/contentstore/management/commands/export_olx.py b/cms/djangoapps/contentstore/management/commands/export_olx.py
index bb11b7ad7b..1f4336fb88 100644
--- a/cms/djangoapps/contentstore/management/commands/export_olx.py
+++ b/cms/djangoapps/contentstore/management/commands/export_olx.py
@@ -67,7 +67,7 @@ class Command(BaseCommand):
def _get_results(self, filename):
"""Load results from file"""
- with open(filename) as f:
+ with open(filename, 'rb') as f: # pylint: disable=open-builtin
results = f.read()
os.remove(filename)
return results
diff --git a/cms/djangoapps/contentstore/management/commands/tests/test_export_olx.py b/cms/djangoapps/contentstore/management/commands/tests/test_export_olx.py
index 021f9fcb9a..b950f89ce0 100644
--- a/cms/djangoapps/contentstore/management/commands/tests/test_export_olx.py
+++ b/cms/djangoapps/contentstore/management/commands/tests/test_export_olx.py
@@ -89,6 +89,13 @@ class TestCourseExportOlx(ModuleStoreTestCase):
with tarfile.open(filename) as tar_file:
self.check_export_file(tar_file, test_course_key)
+ # There is a bug in the underlying management/base code that tries to make
+ # all manageent command output be unicode. This management command
+ # outputs the binary tar file data and so breaks in python3. In python2
+ # the code is happy to pass bytes back and forth and in later versions of
+ # django this is fixed. Howevere it's not possible to get this test to
+ # pass in Python3 and django 1.11
+ @unittest.skip("Bug in django 1.11 prevents this from working in python3. Re-enable after django 2.x upgrade.")
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
def test_export_course_stdout(self, store_type):
test_course_key = self.create_dummy_course(store_type)
diff --git a/cms/djangoapps/contentstore/tasks.py b/cms/djangoapps/contentstore/tasks.py
index 0725ca88c7..01bbee2364 100644
--- a/cms/djangoapps/contentstore/tasks.py
+++ b/cms/djangoapps/contentstore/tasks.py
@@ -749,7 +749,7 @@ def import_olx(self, user_id, course_key_string, archive_path, archive_name, lan
# Locate the uploaded OLX archive (and download it from S3 if necessary)
# Do everything in a try-except block to make sure everything is properly cleaned up.
data_root = path(settings.GITHUB_REPO_ROOT)
- subdir = base64.urlsafe_b64encode(repr(courselike_key))
+ subdir = base64.urlsafe_b64encode(repr(courselike_key).encode('utf-8')).decode('utf-8')
course_dir = data_root / subdir
try:
self.status.set_state(u'Unpacking')
diff --git a/cms/djangoapps/contentstore/tests/test_i18n.py b/cms/djangoapps/contentstore/tests/test_i18n.py
index fa02e2abcb..7a45186b8a 100644
--- a/cms/djangoapps/contentstore/tests/test_i18n.py
+++ b/cms/djangoapps/contentstore/tests/test_i18n.py
@@ -32,6 +32,8 @@ class FakeTranslations(ModuleI18nService):
"""
return self.translations.get(msgid, msgid)
+ gettext = ugettext
+
@staticmethod
def translator(locales_map): # pylint: disable=method-hidden
"""Build mock translator for the given locales.
@@ -123,6 +125,7 @@ class TestModuleI18nService(ModuleStoreTestCase):
self.assertEqual(i18n_service.ugettext(self.test_language), 'dummy language')
@mock.patch('django.utils.translation.ugettext', mock.Mock(return_value='XYZ-TEST-LANGUAGE'))
+ @mock.patch('django.utils.translation.gettext', mock.Mock(return_value='XYZ-TEST-LANGUAGE'))
def test_django_translator_in_use_with_empty_block(self):
"""
Test: Django default translator should in use if we have an empty block
diff --git a/cms/djangoapps/contentstore/tests/test_video_utils.py b/cms/djangoapps/contentstore/tests/test_video_utils.py
index 76ef0e49aa..8747bc9cef 100644
--- a/cms/djangoapps/contentstore/tests/test_video_utils.py
+++ b/cms/djangoapps/contentstore/tests/test_video_utils.py
@@ -319,7 +319,7 @@ class ScrapeVideoThumbnailsTestCase(CourseTestCase):
)
),
(
- 'dummy-content',
+ b'dummy-content',
None,
u'This image file type is not supported. Supported file types are {supported_file_formats}.'.format(
supported_file_formats=list(settings.VIDEO_IMAGE_SUPPORTED_FILE_FORMATS.keys())
diff --git a/cms/djangoapps/contentstore/tests/tests.py b/cms/djangoapps/contentstore/tests/tests.py
index d8a9f1b410..6c5793fa66 100644
--- a/cms/djangoapps/contentstore/tests/tests.py
+++ b/cms/djangoapps/contentstore/tests/tests.py
@@ -335,8 +335,10 @@ class AuthTestCase(ContentStoreTestCase):
is turned off
"""
response = self.client.get(reverse('login'))
- self.assertNotIn('Don't have a Studio Account? Sign up!',
- response.content)
+ self.assertNotContains(
+ response,
+ 'Don't have a Studio Account? Sign up!'
+ )
class ForumTestCase(CourseTestCase):
diff --git a/cms/djangoapps/contentstore/views/import_export.py b/cms/djangoapps/contentstore/views/import_export.py
index fdbc4e176a..3eb9946cd9 100644
--- a/cms/djangoapps/contentstore/views/import_export.py
+++ b/cms/djangoapps/contentstore/views/import_export.py
@@ -117,7 +117,7 @@ def _write_chunk(request, courselike_key):
"""
# Upload .tar.gz to local filesystem for one-server installations not using S3 or Swift
data_root = path(settings.GITHUB_REPO_ROOT)
- subdir = base64.urlsafe_b64encode(repr(courselike_key))
+ subdir = base64.urlsafe_b64encode(repr(courselike_key).encode('utf-8')).decode('utf-8')
course_dir = data_root / subdir
filename = request.FILES['course-data'].name
diff --git a/cms/djangoapps/contentstore/views/tests/test_certificates.py b/cms/djangoapps/contentstore/views/tests/test_certificates.py
index bdd4fe24fe..9f2c54ba58 100644
--- a/cms/djangoapps/contentstore/views/tests/test_certificates.py
+++ b/cms/djangoapps/contentstore/views/tests/test_certificates.py
@@ -295,8 +295,8 @@ class CertificatesListHandlerTestCase(
# in html response
result = self.client.get_html(self._url())
- self.assertIn('Test certificate', result.content)
- self.assertIn('Test description', result.content)
+ self.assertContains(result, 'Test certificate')
+ self.assertContains(result, 'Test description')
# in JSON response
response = self.client.get_json(self._url())
@@ -320,7 +320,7 @@ class CertificatesListHandlerTestCase(
# in html response
result = self.client.get_html(self._url())
- self.assertNotIn('Test certificate', result.content)
+ self.assertNotContains(result, 'Test certificate')
def test_unsupported_http_accept_header(self):
"""
diff --git a/cms/djangoapps/contentstore/views/tests/test_course_updates.py b/cms/djangoapps/contentstore/views/tests/test_course_updates.py
index acdfd28b35..98856e6e07 100644
--- a/cms/djangoapps/contentstore/views/tests/test_course_updates.py
+++ b/cms/djangoapps/contentstore/views/tests/test_course_updates.py
@@ -62,7 +62,7 @@ class CourseUpdateTest(CourseTestCase):
# refetch using provided id
refetched = self.client.get_json(first_update_url)
self.assertHTMLEqual(
- content, json.loads(refetched.content)['content'], "get w/ provided id"
+ content, json.loads(refetched.content.decode('utf-8'))['content'], "get w/ provided id"
)
# now put in an evil update
diff --git a/cms/djangoapps/contentstore/views/tests/test_gating.py b/cms/djangoapps/contentstore/views/tests/test_gating.py
index cc1d96818e..4cf4ddbca7 100644
--- a/cms/djangoapps/contentstore/views/tests/test_gating.py
+++ b/cms/djangoapps/contentstore/views/tests/test_gating.py
@@ -137,7 +137,7 @@ class TestSubsectionGating(CourseTestCase):
{'namespace': '{}{}'.format(six.text_type(self.seq1.location), GATING_NAMESPACE_QUALIFIER)},
{'namespace': '{}{}'.format(six.text_type(self.seq2.location), GATING_NAMESPACE_QUALIFIER)}
]
- resp = json.loads(self.client.get_json(self.seq2_url).content)
+ resp = json.loads(self.client.get_json(self.seq2_url).content.decode('utf-8'))
mock_is_prereq.assert_called_with(self.course.id, self.seq2.location)
mock_get_required_content.assert_called_with(self.course.id, self.seq2.location)
mock_get_prereqs.assert_called_with(self.course.id)
diff --git a/cms/djangoapps/contentstore/views/tests/test_import_export.py b/cms/djangoapps/contentstore/views/tests/test_import_export.py
index 888eb1809b..a7b23ab19b 100644
--- a/cms/djangoapps/contentstore/views/tests/test_import_export.py
+++ b/cms/djangoapps/contentstore/views/tests/test_import_export.py
@@ -94,7 +94,7 @@ class ImportEntranceExamTestCase(CourseTestCase, MilestonesTestCaseMixin):
self.assertIsNotNone(course)
self.assertEquals(course.entrance_exam_enabled, False)
- with open(self.entrance_exam_tar) as gtar:
+ with open(self.entrance_exam_tar, 'rb') as gtar: # pylint: disable=open-builtin
args = {"name": self.entrance_exam_tar, "course-data": [gtar]}
resp = self.client.post(self.url, args)
self.assertEquals(resp.status_code, 200)
@@ -126,7 +126,7 @@ class ImportEntranceExamTestCase(CourseTestCase, MilestonesTestCaseMixin):
self.assertTrue(len(content_milestones))
# Now import entrance exam course
- with open(self.entrance_exam_tar) as gtar:
+ with open(self.entrance_exam_tar, 'rb') as gtar: # pylint: disable=open-builtin
args = {"name": self.entrance_exam_tar, "course-data": [gtar]}
resp = self.client.post(self.url, args)
self.assertEquals(resp.status_code, 200)
@@ -186,7 +186,7 @@ class ImportTestCase(CourseTestCase):
Check that the response for a tar.gz import without a course.xml is
correct.
"""
- with open(self.bad_tar) as btar:
+ with open(self.bad_tar, 'rb') as btar: # pylint: disable=open-builtin
resp = self.client.post(
self.url,
{
@@ -204,14 +204,14 @@ class ImportTestCase(CourseTestCase):
)
)
- self.assertEquals(json.loads(resp_status.content)["ImportStatus"], -2)
+ self.assertEquals(json.loads(resp_status.content.decode('utf-8'))["ImportStatus"], -2)
def test_with_coursexml(self):
"""
Check that the response for a tar.gz import with a course.xml is
correct.
"""
- with open(self.good_tar) as gtar:
+ with open(self.good_tar, 'rb') as gtar: # pylint: disable=open-builtin
args = {"name": self.good_tar, "course-data": [gtar]}
resp = self.client.post(self.url, args)
@@ -230,7 +230,7 @@ class ImportTestCase(CourseTestCase):
display_name_before_import = course.display_name
# Check that global staff user can import course
- with open(self.good_tar) as gtar:
+ with open(self.good_tar, 'rb') as gtar: # pylint: disable=open-builtin
args = {"name": self.good_tar, "course-data": [gtar]}
resp = self.client.post(self.url, args)
self.assertEquals(resp.status_code, 200)
@@ -248,7 +248,7 @@ class ImportTestCase(CourseTestCase):
# Now course staff user can also successfully import course
self.client.login(username=nonstaff_user.username, password='foo')
- with open(self.good_tar) as gtar:
+ with open(self.good_tar, 'rb') as gtar: # pylint: disable=open-builtin
args = {"name": self.good_tar, "course-data": [gtar]}
resp = self.client.post(self.url, args)
self.assertEquals(resp.status_code, 200)
@@ -342,7 +342,7 @@ class ImportTestCase(CourseTestCase):
def try_tar(tarpath):
""" Attempt to tar an unacceptable file """
- with open(tarpath) as tar:
+ with open(tarpath, 'rb') as tar: # pylint: disable=open-builtin
args = {"name": tarpath, "course-data": [tar]}
resp = self.client.post(self.url, args)
self.assertEquals(resp.status_code, 200)
@@ -376,7 +376,7 @@ class ImportTestCase(CourseTestCase):
kwargs={'filename': os.path.split(self.good_tar)[1]}
)
)
- import_status = json.loads(resp_status.content)["ImportStatus"]
+ import_status = json.loads(resp_status.content.decode('utf-8'))["ImportStatus"]
self.assertIn(import_status, (0, 3))
def test_library_import(self):
diff --git a/cms/djangoapps/contentstore/views/tests/test_transcript_settings.py b/cms/djangoapps/contentstore/views/tests/test_transcript_settings.py
index b89e7992a0..f13705a121 100644
--- a/cms/djangoapps/contentstore/views/tests/test_transcript_settings.py
+++ b/cms/djangoapps/contentstore/views/tests/test_transcript_settings.py
@@ -307,7 +307,7 @@ class TranscriptUploadTest(CourseTestCase):
"""
Tests that transcript upload handler works as expected.
"""
- transcript_file_stream = BytesIO('0\n00:00:00,010 --> 00:00:00,100\nПривіт, edX вітає вас.\n\n')
+ transcript_file_stream = six.StringIO('0\n00:00:00,010 --> 00:00:00,100\nПривіт, edX вітає вас.\n\n')
# Make request to transcript upload handler
response = self.client.post(
self.view_url,
@@ -422,7 +422,7 @@ class TranscriptUploadTest(CourseTestCase):
"""
Tests the transcript upload handler with an invalid transcript file.
"""
- transcript_file_stream = BytesIO('An invalid transcript SubRip file content')
+ transcript_file_stream = six.StringIO('An invalid transcript SubRip file content')
# Make request to transcript upload handler
response = self.client.post(
self.view_url,
diff --git a/cms/djangoapps/contentstore/views/tests/test_transcripts.py b/cms/djangoapps/contentstore/views/tests/test_transcripts.py
index 3557c7a21a..4273daef9b 100644
--- a/cms/djangoapps/contentstore/views/tests/test_transcripts.py
+++ b/cms/djangoapps/contentstore/views/tests/test_transcripts.py
@@ -35,7 +35,7 @@ from xmodule.video_module.transcripts_utils import (
TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE)
TEST_DATA_CONTENTSTORE['DOC_STORE_CONFIG']['db'] = 'test_xcontent_%s' % uuid4().hex
-SRT_TRANSCRIPT_CONTENT = b"""0
+SRT_TRANSCRIPT_CONTENT = u"""0
00:00:10,500 --> 00:00:13,000
Elephant's Dream
@@ -160,7 +160,7 @@ class TestUploadTranscripts(BaseTranscripts):
super(TestUploadTranscripts, self).setUp()
self.contents = {
'good': SRT_TRANSCRIPT_CONTENT,
- 'bad': b'Some BAD data',
+ 'bad': u'Some BAD data',
}
# Create temporary transcript files
self.good_srt_file = self.create_transcript_file(content=self.contents['good'], suffix='.srt')
@@ -186,7 +186,7 @@ class TestUploadTranscripts(BaseTranscripts):
Setup a transcript file with suffix and content.
"""
transcript_file = tempfile.NamedTemporaryFile(suffix=suffix)
- wrapped_content = textwrap.dedent(content.decode('utf-8'))
+ wrapped_content = textwrap.dedent(content)
if include_bom:
wrapped_content = wrapped_content.encode('utf-8-sig')
# Verify that ufeff(BOM) character is in content.
@@ -791,7 +791,7 @@ class TestDownloadTranscripts(BaseTranscripts):
"""
self.assertEqual(response.status_code, expected_status_code)
if expected_content:
- self.assertEqual(response.content, expected_content)
+ assert response.content.decode('utf-8') == expected_content
def test_download_youtube_transcript_success(self):
"""
diff --git a/cms/djangoapps/contentstore/views/transcript_settings.py b/cms/djangoapps/contentstore/views/transcript_settings.py
index d4457a63f6..03105f005b 100644
--- a/cms/djangoapps/contentstore/views/transcript_settings.py
+++ b/cms/djangoapps/contentstore/views/transcript_settings.py
@@ -229,7 +229,7 @@ def transcript_upload_handler(request):
# Convert SRT transcript into an SJSON format
# and upload it to S3.
sjson_subs = Transcript.convert(
- content=transcript_file.read(),
+ content=transcript_file.read().decode('utf-8'),
input_format=Transcript.SRT,
output_format=Transcript.SJSON
)
diff --git a/cms/djangoapps/contentstore/views/transcripts_ajax.py b/cms/djangoapps/contentstore/views/transcripts_ajax.py
index 8c1a76b722..4510aa84ae 100644
--- a/cms/djangoapps/contentstore/views/transcripts_ajax.py
+++ b/cms/djangoapps/contentstore/views/transcripts_ajax.py
@@ -219,7 +219,7 @@ def upload_transcripts(request):
# Convert 'srt' transcript into the 'sjson' and upload it to
# configured transcript storage. For example, S3.
sjson_subs = Transcript.convert(
- content=transcript_file.read(),
+ content=transcript_file.read().decode('utf-8'),
input_format=Transcript.SRT,
output_format=Transcript.SJSON
)
@@ -322,7 +322,7 @@ def check_transcripts(request):
filename = 'subs_{0}.srt.sjson'.format(item.sub)
content_location = StaticContent.compute_location(item.location.course_key, filename)
try:
- local_transcripts = contentstore().find(content_location).data
+ local_transcripts = contentstore().find(content_location).data.decode('utf-8')
transcripts_presence['current_item_subs'] = item.sub
except NotFoundError:
pass
@@ -336,7 +336,7 @@ def check_transcripts(request):
filename = 'subs_{0}.srt.sjson'.format(youtube_id)
content_location = StaticContent.compute_location(item.location.course_key, filename)
try:
- local_transcripts = contentstore().find(content_location).data
+ local_transcripts = contentstore().find(content_location).data.decode('utf-8')
transcripts_presence['youtube_local'] = True
except NotFoundError:
log.debug(u"Can't find transcripts in storage for youtube id: %s", youtube_id)
diff --git a/common/djangoapps/student/admin.py b/common/djangoapps/student/admin.py
index c6a0888779..b24d395ed4 100644
--- a/common/djangoapps/student/admin.py
+++ b/common/djangoapps/student/admin.py
@@ -37,7 +37,8 @@ from student.models import (
RegistrationCookieConfiguration,
UserAttribute,
UserProfile,
- UserTestGroup
+ UserTestGroup,
+ BulkUnenrollConfiguration
)
from student.roles import REGISTERED_ACCESS_ROLES
from xmodule.modulestore.django import modulestore
@@ -445,6 +446,7 @@ admin.site.register(Registration)
admin.site.register(PendingNameChange)
admin.site.register(DashboardConfiguration, ConfigurationModelAdmin)
admin.site.register(RegistrationCookieConfiguration, ConfigurationModelAdmin)
+admin.site.register(BulkUnenrollConfiguration, ConfigurationModelAdmin)
# We must first un-register the User model since it may also be registered by the auth app.
diff --git a/common/djangoapps/student/management/commands/bulk_unenroll.py b/common/djangoapps/student/management/commands/bulk_unenroll.py
index 6ac0a4f0a1..e9932afe85 100644
--- a/common/djangoapps/student/management/commands/bulk_unenroll.py
+++ b/common/djangoapps/student/management/commands/bulk_unenroll.py
@@ -5,11 +5,9 @@ import logging
import unicodecsv
from django.core.exceptions import ObjectDoesNotExist
from django.core.management.base import BaseCommand
-from django.db.models import Q
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
-
-from student.models import CourseEnrollment, User
+from student.models import CourseEnrollment, User, BulkUnenrollConfiguration
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
@@ -27,39 +25,48 @@ class Command(BaseCommand):
parser.add_argument('-p', '--csv_path',
metavar='csv_path',
dest='csv_path',
- required=True,
+ required=False,
help='Path to CSV file.')
def handle(self, *args, **options):
+
csv_path = options['csv_path']
- with open(csv_path, 'rb') as csvfile:
- reader = unicodecsv.DictReader(csvfile)
- for row in reader:
- username = row['username']
- email = row['email']
- course_key = row['course_id']
- try:
- user = User.objects.get(Q(username=username) | Q(email=email))
- except ObjectDoesNotExist:
- user = None
- msg = 'User with username {} or email {} does not exist'.format(username, email)
- logger.warning(msg)
+ if csv_path:
+ with open(csv_path) as csv_file:
+ self.unenroll_users(csv_file)
+ else:
+ csv_file = BulkUnenrollConfiguration.current().csv_file
+ self.unenroll_users(csv_file)
- try:
- course_id = CourseKey.from_string(course_key)
- except InvalidKeyError:
- course_id = None
- msg = 'Invalid course id {course_id}, skipping un-enrollement for {username}, {email}'.format(**row)
- logger.warning(msg)
+ def unenroll_users(self, csv_file):
+ reader = list(unicodecsv.DictReader(csv_file))
+ users_unenrolled = {}
+ for row in reader:
+ username = row['username']
+ course_key = row['course_id']
- if user and course_id:
- enrollment = CourseEnrollment.get_enrollment(user, course_id)
- if not enrollment:
- msg = 'Enrollment for the user {} in course {} does not exist!'.format(username, course_key)
- logger.info(msg)
- else:
- try:
- CourseEnrollment.unenroll(user, course_id, skip_refund=True)
- except Exception as err:
- msg = 'Error un-enrolling User {} from course {}: '.format(username, course_key, err)
- logger.error(msg, exc_info=True)
+ try:
+ course_id = CourseKey.from_string(row['course_id'])
+ except InvalidKeyError:
+ msg = 'Invalid course id {course_id}, skipping un-enrollement for {username}, {email}'.format(**row)
+ logger.warning(msg)
+ continue
+
+ try:
+ enrollment = CourseEnrollment.objects.get(user__username=username, course_id=course_id)
+ enrollment.update_enrollment(is_active=False, skip_refund=True)
+ if username in users_unenrolled:
+ users_unenrolled[username].append(course_key.encode())
+ else:
+ users_unenrolled[username] = [course_key.encode()]
+
+ except ObjectDoesNotExist:
+ msg = 'Enrollment for the user {} in course {} does not exist!'.format(username, course_key)
+ logger.info(msg)
+
+ except Exception as err:
+ msg = 'Error un-enrolling User {} from course {}: '.format(username, course_key, err)
+ logger.error(msg, exc_info=True)
+
+ logger.info("Following users have been unenrolled successfully from the following courses: {users_unenrolled}"
+ .format(users_unenrolled=["{}:{}".format(k, v) for k, v in users_unenrolled.items()]))
diff --git a/common/djangoapps/student/management/tests/test_bulk_unenroll.py b/common/djangoapps/student/management/tests/test_bulk_unenroll.py
index 5a402e1968..3417b7c1a2 100644
--- a/common/djangoapps/student/management/tests/test_bulk_unenroll.py
+++ b/common/djangoapps/student/management/tests/test_bulk_unenroll.py
@@ -1,14 +1,14 @@
from __future__ import absolute_import
import six
+
from tempfile import NamedTemporaryFile
-
-from django.core.management import call_command
-from testfixtures import LogCapture
-
from course_modes.tests.factories import CourseModeFactory
-from student.models import CourseEnrollment
+from django.core.files.uploadedfile import SimpleUploadedFile
+from django.core.management import call_command
+from student.models import CourseEnrollment, BulkUnenrollConfiguration
from student.tests.factories import UserFactory
+from testfixtures import LogCapture
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -47,21 +47,6 @@ class BulkUnenrollTests(SharedModuleStoreTestCase):
csv.seek(0)
return csv
- def test_user_not_exist(self):
- """Verify that warning user not exist is logged for non existing user."""
- with NamedTemporaryFile() as csv:
- csv = self._write_test_csv(csv, lines=["111,test,test@example.com,course-v1:edX+DemoX+Demo_Course\n"])
-
- with LogCapture(LOGGER_NAME) as log:
- call_command("bulk_unenroll", "--csv_path={}".format(csv.name))
- log.check(
- (
- LOGGER_NAME,
- 'WARNING',
- 'User with username {} or email {} does not exist'.format('test', 'test@example.com')
- )
- )
-
def test_invalid_course_key(self):
"""Verify in case of invalid course key warning is logged."""
with NamedTemporaryFile() as csv:
@@ -69,13 +54,11 @@ class BulkUnenrollTests(SharedModuleStoreTestCase):
with LogCapture(LOGGER_NAME) as log:
call_command("bulk_unenroll", "--csv_path={}".format(csv.name))
- log.check(
- (
- LOGGER_NAME,
- 'WARNING',
- 'Invalid course id {}, skipping un-enrollement for {}, {}'.format(
- 'test_course', 'amy', 'amy@pond.com')
- )
+ expected_message = 'Invalid course id {}, skipping un-enrollement for {}, {}'.\
+ format('test_course', 'amy', 'amy@pond.com')
+
+ log.check_present(
+ (LOGGER_NAME, 'WARNING', expected_message)
)
def test_user_not_enrolled(self):
@@ -85,13 +68,11 @@ class BulkUnenrollTests(SharedModuleStoreTestCase):
with LogCapture(LOGGER_NAME) as log:
call_command("bulk_unenroll", "--csv_path={}".format(csv.name))
- log.check(
- (
- LOGGER_NAME,
- 'INFO',
- 'Enrollment for the user {} in course {} does not exist!'.format(
- 'amy', 'course-v1:edX+DemoX+Demo_Course')
- )
+ expected_message = 'Enrollment for the user {} in course {} does not exist!'.\
+ format('amy', 'course-v1:edX+DemoX+Demo_Course')
+
+ log.check_present(
+ (LOGGER_NAME, 'INFO', expected_message)
)
def test_bulk_un_enroll(self):
@@ -107,3 +88,46 @@ class BulkUnenrollTests(SharedModuleStoreTestCase):
call_command("bulk_unenroll", "--csv_path={}".format(csv.name))
for enrollment in CourseEnrollment.objects.all():
self.assertEqual(enrollment.is_active, False)
+
+ def test_bulk_unenroll_from_config_model(self):
+ """Verify users are unenrolled using the command."""
+ lines = "user_id,username,email,course_id\n"
+ for enrollment in self.enrollments:
+ lines += str(enrollment.user.id) + "," + enrollment.user.username + "," + \
+ enrollment.user.email + "," + str(enrollment.course.id) + "\n"
+
+ csv_file = SimpleUploadedFile(name='test.csv', content=lines, content_type='text/csv')
+ BulkUnenrollConfiguration.objects.create(enabled=True, csv_file=csv_file)
+
+ call_command("bulk_unenroll")
+ for enrollment in CourseEnrollment.objects.all():
+ self.assertEqual(enrollment.is_active, False)
+
+ def test_users_unenroll_successfully_logged(self):
+ """Verify users unenrolled are logged """
+ lines = "user_id,username,email,course_id\n"
+ users_unenrolled = {}
+ for enrollment in self.enrollments:
+ username = enrollment.user.username
+ if username in users_unenrolled:
+ users_unenrolled[username].append(str(enrollment.course.id))
+ else:
+ users_unenrolled[username] = [str(enrollment.course.id)]
+
+ lines += str(enrollment.user.id) + "," + username + "," + \
+ enrollment.user.email + "," + str(enrollment.course.id) + "\n"
+
+ csv_file = SimpleUploadedFile(name='test.csv', content=lines, content_type='text/csv')
+ BulkUnenrollConfiguration.objects.create(enabled=True, csv_file=csv_file)
+
+ with LogCapture(LOGGER_NAME) as log:
+ call_command("bulk_unenroll")
+ log.check(
+ (
+ LOGGER_NAME,
+ 'INFO',
+ 'Following users have been unenrolled successfully from the following courses:'
+ ' {users_unenrolled}'.format(users_unenrolled=["{}:{}".format(k, v) for k, v in
+ users_unenrolled.items()])
+ )
+ )
diff --git a/common/djangoapps/student/migrations/0023_bulkunenrollconfiguration.py b/common/djangoapps/student/migrations/0023_bulkunenrollconfiguration.py
new file mode 100644
index 0000000000..774a70b254
--- /dev/null
+++ b/common/djangoapps/student/migrations/0023_bulkunenrollconfiguration.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.24 on 2019-09-19 19:51
+from __future__ import unicode_literals
+
+from django.conf import settings
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('student', '0022_indexing_in_courseenrollment'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='BulkUnenrollConfiguration',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
+ ('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
+ ('csv_file', models.FileField(help_text='It expect that the data will be provided in a csv file format with first row being the header and columns will be as follows: user_id, username, email, course_id, is_verified, verification_date', upload_to=b'', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=[b'csv'])])),
+ ('changed_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Changed by')),
+ ],
+ options={
+ 'ordering': ('-change_date',),
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py
index 4a29fe8eff..a47b608eda 100644
--- a/common/djangoapps/student/models.py
+++ b/common/djangoapps/student/models.py
@@ -18,6 +18,7 @@ import logging
import uuid
from collections import OrderedDict, defaultdict, namedtuple
from datetime import datetime, timedelta
+from django.core.validators import FileExtensionValidator
from functools import total_ordering
from importlib import import_module
@@ -2845,6 +2846,18 @@ class RegistrationCookieConfiguration(ConfigurationModel):
)
+class BulkUnenrollConfiguration(ConfigurationModel):
+ """
+
+ """
+ csv_file = models.FileField(
+ validators=[FileExtensionValidator(allowed_extensions=['csv'])],
+ help_text=_("It expect that the data will be provided in a csv file format with \
+ first row being the header and columns will be as follows: \
+ user_id, username, email, course_id, is_verified, verification_date")
+ )
+
+
@python_2_unicode_compatible
class UserAttribute(TimeStampedModel):
"""
diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py
index b0c5829d01..cd2b81fc1c 100644
--- a/common/lib/capa/capa/responsetypes.py
+++ b/common/lib/capa/capa/responsetypes.py
@@ -19,7 +19,7 @@ import inspect
import json
import logging
import numbers
-import random
+import random2 as random
import re
import sys
import textwrap
diff --git a/common/lib/capa/capa/tests/test_responsetypes.py b/common/lib/capa/capa/tests/test_responsetypes.py
index be9c88c13f..caeca6f07b 100644
--- a/common/lib/capa/capa/tests/test_responsetypes.py
+++ b/common/lib/capa/capa/tests/test_responsetypes.py
@@ -8,7 +8,7 @@ from __future__ import absolute_import
import io
import json
import os
-import random
+import random2 as random
import textwrap
import unittest
import zipfile
diff --git a/common/lib/capa/capa/tests/test_shuffle.py b/common/lib/capa/capa/tests/test_shuffle.py
index 42109642ca..11f8445b42 100644
--- a/common/lib/capa/capa/tests/test_shuffle.py
+++ b/common/lib/capa/capa/tests/test_shuffle.py
@@ -3,7 +3,6 @@ from __future__ import absolute_import, print_function
import textwrap
import unittest
-import six
from capa.responsetypes import LoncapaProblemError
from capa.tests.helpers import new_loncapa_problem, test_capa_system
@@ -59,10 +58,7 @@ class CapaShuffleTest(unittest.TestCase):
response = list(problem.responders.values())[0]
self.assertFalse(response.has_mask())
self.assertTrue(response.has_shuffle())
- if six.PY2:
- self.assertEqual(response.unmask_order(), ['choice_0', 'choice_aaa', 'choice_1', 'choice_ddd'])
- else:
- self.assertEqual(response.unmask_order(), ['choice_1', 'choice_aaa', 'choice_0', 'choice_ddd'])
+ self.assertEqual(response.unmask_order(), ['choice_0', 'choice_aaa', 'choice_1', 'choice_ddd'])
def test_shuffle_different_seed(self):
xml_str = textwrap.dedent("""
diff --git a/common/lib/xmodule/xmodule/contentstore/mongo.py b/common/lib/xmodule/xmodule/contentstore/mongo.py
index 5ff01ddfae..ae747f97fa 100644
--- a/common/lib/xmodule/xmodule/contentstore/mongo.py
+++ b/common/lib/xmodule/xmodule/contentstore/mongo.py
@@ -101,12 +101,19 @@ class MongoContentStore(ContentStore):
locked=getattr(content, 'locked', False)) as fp:
# It seems that this code thought that only some specific object would have the `__iter__` attribute
- # but the bytes object in python 3 has one and should not use the chunking logic.
- if hasattr(content.data, '__iter__') and not isinstance(content.data, six.binary_type):
+ # but many more objects have this in python3 and shouldn't be using the chunking logic. For string and
+ # byte streams we write them directly to gridfs and convert them to byetarrys if necessary.
+ if hasattr(content.data, '__iter__') and not isinstance(content.data, (six.binary_type, six.string_types)):
for chunk in content.data:
fp.write(chunk)
else:
- fp.write(content.data)
+ # Ideally we could just ensure that we don't get strings in here and only byte streams
+ # but being confident of that wolud be a lot more work than we have time for so we just
+ # handle both cases here.
+ if isinstance(content.data, six.text_type):
+ fp.write(content.data.encode('utf-8'))
+ else:
+ fp.write(content.data)
return content
diff --git a/common/lib/xmodule/xmodule/contentstore/utils.py b/common/lib/xmodule/xmodule/contentstore/utils.py
index 5baafcd74f..b7c1413661 100644
--- a/common/lib/xmodule/xmodule/contentstore/utils.py
+++ b/common/lib/xmodule/xmodule/contentstore/utils.py
@@ -16,13 +16,13 @@ def empty_asset_trashcan(course_locs):
thumbs = store.get_all_content_thumbnails_for_course(course_loc)
for thumb in thumbs:
print("Deleting {0}...".format(thumb))
- store.delete(thumb['_id'])
+ store.delete(thumb['asset_key'])
# then delete all of the assets
assets, __ = store.get_all_content_for_course(course_loc)
for asset in assets:
print("Deleting {0}...".format(asset))
- store.delete(asset['_id'])
+ store.delete(asset['asset_key'])
def restore_asset_from_trashcan(location):
diff --git a/common/lib/xmodule/xmodule/video_module/transcripts_utils.py b/common/lib/xmodule/xmodule/video_module/transcripts_utils.py
index 1e334ffc20..87b11c953b 100644
--- a/common/lib/xmodule/xmodule/video_module/transcripts_utils.py
+++ b/common/lib/xmodule/xmodule/video_module/transcripts_utils.py
@@ -145,7 +145,7 @@ def youtube_video_transcript_name(youtube_text_api):
# http://video.google.com/timedtext?type=list&v={VideoId}
youtube_response = requests.get('http://' + youtube_text_api['url'], params=transcripts_param)
if youtube_response.status_code == 200 and youtube_response.text:
- youtube_data = etree.fromstring(youtube_response.content.encode('utf-8'), parser=utf8_parser)
+ youtube_data = etree.fromstring(youtube_response.text.encode('utf-8'), parser=utf8_parser)
# iterate all transcripts information from youtube server
for element in youtube_data:
# search specific language code such as 'en' in transcripts info list
@@ -346,7 +346,7 @@ def copy_or_rename_transcript(new_name, old_name, item, delete_old=False, user=N
"""
filename = u'subs_{0}.srt.sjson'.format(old_name)
content_location = StaticContent.compute_location(item.location.course_key, filename)
- transcripts = contentstore().find(content_location).data
+ transcripts = contentstore().find(content_location).data.decode('utf-8')
save_subs_to_store(json.loads(transcripts), new_name, item)
item.sub = new_name
item.save_with_metadata(user)
@@ -579,6 +579,8 @@ def get_video_transcript_content(edx_video_id, language_code):
edx_video_id = clean_video_id(edx_video_id)
if edxval_api and edx_video_id:
transcript = edxval_api.get_video_transcript_data(edx_video_id, language_code)
+ if transcript and 'content' in transcript:
+ transcript['content'] = transcript['content'].decode('utf-8')
return transcript
@@ -654,7 +656,7 @@ class Transcript(object):
if input_format == 'srt':
if output_format == 'txt':
- text = SubRipFile.from_string(content.decode('utf-8')).text
+ text = SubRipFile.from_string(content).text
return HTMLParser().unescape(text)
elif output_format == 'sjson':
@@ -663,7 +665,7 @@ class Transcript(object):
# the exception if something went wrong in parsing the transcript.
srt_subs = SubRipFile.from_string(
# Skip byte order mark(BOM) character
- content.decode('utf-8-sig') if six.PY2 else content.encode('utf-8').decode('utf-8-sig'),
+ content.encode('utf-8').decode('utf-8-sig'),
error_handling=SubRipFile.ERROR_RAISE
)
except Error as ex: # Base exception from pysrt
@@ -925,11 +927,11 @@ def get_transcript_for_video(video_location, subs_id, file_name, language):
try:
if subs_id is None:
raise NotFoundError
- content = Transcript.asset(video_location, subs_id, language).data
+ content = Transcript.asset(video_location, subs_id, language).data.decode('utf-8')
base_name = subs_id
input_format = Transcript.SJSON
except NotFoundError:
- content = Transcript.asset(video_location, None, language, file_name).data
+ content = Transcript.asset(video_location, None, language, file_name).data.decode('utf-8')
base_name = os.path.splitext(file_name)[0]
input_format = Transcript.SRT
diff --git a/common/lib/xmodule/xmodule/video_module/video_handlers.py b/common/lib/xmodule/xmodule/video_module/video_handlers.py
index 386ca42240..d31dba8a98 100644
--- a/common/lib/xmodule/xmodule/video_module/video_handlers.py
+++ b/common/lib/xmodule/xmodule/video_module/video_handlers.py
@@ -466,7 +466,7 @@ class VideoStudioViewHandlers(object):
# Convert SRT transcript into an SJSON format
# and upload it to S3.
sjson_subs = Transcript.convert(
- content=transcript_file.read(),
+ content=transcript_file.read().decode('utf-8'),
input_format=Transcript.SRT,
output_format=Transcript.SJSON
)
diff --git a/common/test/db_cache/bok_choy_data_default.json b/common/test/db_cache/bok_choy_data_default.json
index 699623b92d..a1ec5abf87 100644
--- a/common/test/db_cache/bok_choy_data_default.json
+++ b/common/test/db_cache/bok_choy_data_default.json
@@ -1 +1 @@
-[{"model": "contenttypes.contenttype", "pk": 1, "fields": {"app_label": "api_admin", "model": "apiaccessrequest"}}, {"model": "contenttypes.contenttype", "pk": 2, "fields": {"app_label": "auth", "model": "permission"}}, {"model": "contenttypes.contenttype", "pk": 3, "fields": {"app_label": "auth", "model": "group"}}, {"model": "contenttypes.contenttype", "pk": 4, "fields": {"app_label": "auth", "model": "user"}}, {"model": "contenttypes.contenttype", "pk": 5, "fields": {"app_label": "contenttypes", "model": "contenttype"}}, {"model": "contenttypes.contenttype", "pk": 6, "fields": {"app_label": "redirects", "model": "redirect"}}, {"model": "contenttypes.contenttype", "pk": 7, "fields": {"app_label": "sessions", "model": "session"}}, {"model": "contenttypes.contenttype", "pk": 8, "fields": {"app_label": "sites", "model": "site"}}, {"model": "contenttypes.contenttype", "pk": 9, "fields": {"app_label": "djcelery", "model": "taskmeta"}}, {"model": "contenttypes.contenttype", "pk": 10, "fields": {"app_label": "djcelery", "model": "tasksetmeta"}}, {"model": "contenttypes.contenttype", "pk": 11, "fields": {"app_label": "djcelery", "model": "intervalschedule"}}, {"model": "contenttypes.contenttype", "pk": 12, "fields": {"app_label": "djcelery", "model": "crontabschedule"}}, {"model": "contenttypes.contenttype", "pk": 13, "fields": {"app_label": "djcelery", "model": "periodictasks"}}, {"model": "contenttypes.contenttype", "pk": 14, "fields": {"app_label": "djcelery", "model": "periodictask"}}, {"model": "contenttypes.contenttype", "pk": 15, "fields": {"app_label": "djcelery", "model": "workerstate"}}, {"model": "contenttypes.contenttype", "pk": 16, "fields": {"app_label": "djcelery", "model": "taskstate"}}, {"model": "contenttypes.contenttype", "pk": 17, "fields": {"app_label": "waffle", "model": "flag"}}, {"model": "contenttypes.contenttype", "pk": 18, "fields": {"app_label": "waffle", "model": "switch"}}, {"model": "contenttypes.contenttype", "pk": 19, "fields": {"app_label": "waffle", "model": "sample"}}, {"model": "contenttypes.contenttype", "pk": 20, "fields": {"app_label": "status", "model": "globalstatusmessage"}}, {"model": "contenttypes.contenttype", "pk": 21, "fields": {"app_label": "status", "model": "coursemessage"}}, {"model": "contenttypes.contenttype", "pk": 22, "fields": {"app_label": "static_replace", "model": "assetbaseurlconfig"}}, {"model": "contenttypes.contenttype", "pk": 23, "fields": {"app_label": "static_replace", "model": "assetexcludedextensionsconfig"}}, {"model": "contenttypes.contenttype", "pk": 24, "fields": {"app_label": "contentserver", "model": "courseassetcachettlconfig"}}, {"model": "contenttypes.contenttype", "pk": 25, "fields": {"app_label": "contentserver", "model": "cdnuseragentsconfig"}}, {"model": "contenttypes.contenttype", "pk": 26, "fields": {"app_label": "theming", "model": "sitetheme"}}, {"model": "contenttypes.contenttype", "pk": 27, "fields": {"app_label": "site_configuration", "model": "siteconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 28, "fields": {"app_label": "site_configuration", "model": "siteconfigurationhistory"}}, {"model": "contenttypes.contenttype", "pk": 29, "fields": {"app_label": "video_config", "model": "hlsplaybackenabledflag"}}, {"model": "contenttypes.contenttype", "pk": 30, "fields": {"app_label": "video_config", "model": "coursehlsplaybackenabledflag"}}, {"model": "contenttypes.contenttype", "pk": 31, "fields": {"app_label": "video_config", "model": "videotranscriptenabledflag"}}, {"model": "contenttypes.contenttype", "pk": 32, "fields": {"app_label": "video_config", "model": "coursevideotranscriptenabledflag"}}, {"model": "contenttypes.contenttype", "pk": 33, "fields": {"app_label": "video_pipeline", "model": "videopipelineintegration"}}, {"model": "contenttypes.contenttype", "pk": 34, "fields": {"app_label": "video_pipeline", "model": "videouploadsenabledbydefault"}}, {"model": "contenttypes.contenttype", "pk": 35, "fields": {"app_label": "video_pipeline", "model": "coursevideouploadsenabledbydefault"}}, {"model": "contenttypes.contenttype", "pk": 36, "fields": {"app_label": "bookmarks", "model": "bookmark"}}, {"model": "contenttypes.contenttype", "pk": 37, "fields": {"app_label": "bookmarks", "model": "xblockcache"}}, {"model": "contenttypes.contenttype", "pk": 38, "fields": {"app_label": "courseware", "model": "studentmodule"}}, {"model": "contenttypes.contenttype", "pk": 39, "fields": {"app_label": "courseware", "model": "studentmodulehistory"}}, {"model": "contenttypes.contenttype", "pk": 40, "fields": {"app_label": "courseware", "model": "xmoduleuserstatesummaryfield"}}, {"model": "contenttypes.contenttype", "pk": 41, "fields": {"app_label": "courseware", "model": "xmodulestudentprefsfield"}}, {"model": "contenttypes.contenttype", "pk": 42, "fields": {"app_label": "courseware", "model": "xmodulestudentinfofield"}}, {"model": "contenttypes.contenttype", "pk": 43, "fields": {"app_label": "courseware", "model": "offlinecomputedgrade"}}, {"model": "contenttypes.contenttype", "pk": 44, "fields": {"app_label": "courseware", "model": "offlinecomputedgradelog"}}, {"model": "contenttypes.contenttype", "pk": 45, "fields": {"app_label": "courseware", "model": "studentfieldoverride"}}, {"model": "contenttypes.contenttype", "pk": 46, "fields": {"app_label": "courseware", "model": "dynamicupgradedeadlineconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 47, "fields": {"app_label": "courseware", "model": "coursedynamicupgradedeadlineconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 48, "fields": {"app_label": "courseware", "model": "orgdynamicupgradedeadlineconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 49, "fields": {"app_label": "student", "model": "anonymoususerid"}}, {"model": "contenttypes.contenttype", "pk": 50, "fields": {"app_label": "student", "model": "userstanding"}}, {"model": "contenttypes.contenttype", "pk": 51, "fields": {"app_label": "student", "model": "userprofile"}}, {"model": "contenttypes.contenttype", "pk": 52, "fields": {"app_label": "student", "model": "usersignupsource"}}, {"model": "contenttypes.contenttype", "pk": 53, "fields": {"app_label": "student", "model": "usertestgroup"}}, {"model": "contenttypes.contenttype", "pk": 54, "fields": {"app_label": "student", "model": "registration"}}, {"model": "contenttypes.contenttype", "pk": 55, "fields": {"app_label": "student", "model": "pendingnamechange"}}, {"model": "contenttypes.contenttype", "pk": 56, "fields": {"app_label": "student", "model": "pendingemailchange"}}, {"model": "contenttypes.contenttype", "pk": 57, "fields": {"app_label": "student", "model": "passwordhistory"}}, {"model": "contenttypes.contenttype", "pk": 58, "fields": {"app_label": "student", "model": "loginfailures"}}, {"model": "contenttypes.contenttype", "pk": 59, "fields": {"app_label": "student", "model": "courseenrollment"}}, {"model": "contenttypes.contenttype", "pk": 60, "fields": {"app_label": "student", "model": "manualenrollmentaudit"}}, {"model": "contenttypes.contenttype", "pk": 61, "fields": {"app_label": "student", "model": "courseenrollmentallowed"}}, {"model": "contenttypes.contenttype", "pk": 62, "fields": {"app_label": "student", "model": "courseaccessrole"}}, {"model": "contenttypes.contenttype", "pk": 63, "fields": {"app_label": "student", "model": "dashboardconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 64, "fields": {"app_label": "student", "model": "linkedinaddtoprofileconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 65, "fields": {"app_label": "student", "model": "entranceexamconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 66, "fields": {"app_label": "student", "model": "languageproficiency"}}, {"model": "contenttypes.contenttype", "pk": 67, "fields": {"app_label": "student", "model": "sociallink"}}, {"model": "contenttypes.contenttype", "pk": 68, "fields": {"app_label": "student", "model": "courseenrollmentattribute"}}, {"model": "contenttypes.contenttype", "pk": 69, "fields": {"app_label": "student", "model": "enrollmentrefundconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 70, "fields": {"app_label": "student", "model": "registrationcookieconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 71, "fields": {"app_label": "student", "model": "userattribute"}}, {"model": "contenttypes.contenttype", "pk": 72, "fields": {"app_label": "student", "model": "logoutviewconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 73, "fields": {"app_label": "track", "model": "trackinglog"}}, {"model": "contenttypes.contenttype", "pk": 74, "fields": {"app_label": "util", "model": "ratelimitconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 75, "fields": {"app_label": "certificates", "model": "certificatewhitelist"}}, {"model": "contenttypes.contenttype", "pk": 76, "fields": {"app_label": "certificates", "model": "generatedcertificate"}}, {"model": "contenttypes.contenttype", "pk": 77, "fields": {"app_label": "certificates", "model": "certificategenerationhistory"}}, {"model": "contenttypes.contenttype", "pk": 78, "fields": {"app_label": "certificates", "model": "certificateinvalidation"}}, {"model": "contenttypes.contenttype", "pk": 79, "fields": {"app_label": "certificates", "model": "examplecertificateset"}}, {"model": "contenttypes.contenttype", "pk": 80, "fields": {"app_label": "certificates", "model": "examplecertificate"}}, {"model": "contenttypes.contenttype", "pk": 81, "fields": {"app_label": "certificates", "model": "certificategenerationcoursesetting"}}, {"model": "contenttypes.contenttype", "pk": 82, "fields": {"app_label": "certificates", "model": "certificategenerationconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 83, "fields": {"app_label": "certificates", "model": "certificatehtmlviewconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 84, "fields": {"app_label": "certificates", "model": "certificatetemplate"}}, {"model": "contenttypes.contenttype", "pk": 85, "fields": {"app_label": "certificates", "model": "certificatetemplateasset"}}, {"model": "contenttypes.contenttype", "pk": 86, "fields": {"app_label": "instructor_task", "model": "instructortask"}}, {"model": "contenttypes.contenttype", "pk": 87, "fields": {"app_label": "instructor_task", "model": "gradereportsetting"}}, {"model": "contenttypes.contenttype", "pk": 88, "fields": {"app_label": "course_groups", "model": "courseusergroup"}}, {"model": "contenttypes.contenttype", "pk": 89, "fields": {"app_label": "course_groups", "model": "cohortmembership"}}, {"model": "contenttypes.contenttype", "pk": 90, "fields": {"app_label": "course_groups", "model": "courseusergrouppartitiongroup"}}, {"model": "contenttypes.contenttype", "pk": 91, "fields": {"app_label": "course_groups", "model": "coursecohortssettings"}}, {"model": "contenttypes.contenttype", "pk": 92, "fields": {"app_label": "course_groups", "model": "coursecohort"}}, {"model": "contenttypes.contenttype", "pk": 93, "fields": {"app_label": "course_groups", "model": "unregisteredlearnercohortassignments"}}, {"model": "contenttypes.contenttype", "pk": 94, "fields": {"app_label": "bulk_email", "model": "target"}}, {"model": "contenttypes.contenttype", "pk": 95, "fields": {"app_label": "bulk_email", "model": "cohorttarget"}}, {"model": "contenttypes.contenttype", "pk": 96, "fields": {"app_label": "bulk_email", "model": "coursemodetarget"}}, {"model": "contenttypes.contenttype", "pk": 97, "fields": {"app_label": "bulk_email", "model": "courseemail"}}, {"model": "contenttypes.contenttype", "pk": 98, "fields": {"app_label": "bulk_email", "model": "optout"}}, {"model": "contenttypes.contenttype", "pk": 99, "fields": {"app_label": "bulk_email", "model": "courseemailtemplate"}}, {"model": "contenttypes.contenttype", "pk": 100, "fields": {"app_label": "bulk_email", "model": "courseauthorization"}}, {"model": "contenttypes.contenttype", "pk": 101, "fields": {"app_label": "bulk_email", "model": "bulkemailflag"}}, {"model": "contenttypes.contenttype", "pk": 102, "fields": {"app_label": "branding", "model": "brandinginfoconfig"}}, {"model": "contenttypes.contenttype", "pk": 103, "fields": {"app_label": "branding", "model": "brandingapiconfig"}}, {"model": "contenttypes.contenttype", "pk": 104, "fields": {"app_label": "grades", "model": "visibleblocks"}}, {"model": "contenttypes.contenttype", "pk": 105, "fields": {"app_label": "grades", "model": "persistentsubsectiongrade"}}, {"model": "contenttypes.contenttype", "pk": 106, "fields": {"app_label": "grades", "model": "persistentcoursegrade"}}, {"model": "contenttypes.contenttype", "pk": 107, "fields": {"app_label": "grades", "model": "persistentsubsectiongradeoverride"}}, {"model": "contenttypes.contenttype", "pk": 108, "fields": {"app_label": "grades", "model": "persistentgradesenabledflag"}}, {"model": "contenttypes.contenttype", "pk": 109, "fields": {"app_label": "grades", "model": "coursepersistentgradesflag"}}, {"model": "contenttypes.contenttype", "pk": 110, "fields": {"app_label": "grades", "model": "computegradessetting"}}, {"model": "contenttypes.contenttype", "pk": 111, "fields": {"app_label": "external_auth", "model": "externalauthmap"}}, {"model": "contenttypes.contenttype", "pk": 112, "fields": {"app_label": "django_openid_auth", "model": "nonce"}}, {"model": "contenttypes.contenttype", "pk": 113, "fields": {"app_label": "django_openid_auth", "model": "association"}}, {"model": "contenttypes.contenttype", "pk": 114, "fields": {"app_label": "django_openid_auth", "model": "useropenid"}}, {"model": "contenttypes.contenttype", "pk": 115, "fields": {"app_label": "oauth2", "model": "client"}}, {"model": "contenttypes.contenttype", "pk": 116, "fields": {"app_label": "oauth2", "model": "grant"}}, {"model": "contenttypes.contenttype", "pk": 117, "fields": {"app_label": "oauth2", "model": "accesstoken"}}, {"model": "contenttypes.contenttype", "pk": 118, "fields": {"app_label": "oauth2", "model": "refreshtoken"}}, {"model": "contenttypes.contenttype", "pk": 119, "fields": {"app_label": "edx_oauth2_provider", "model": "trustedclient"}}, {"model": "contenttypes.contenttype", "pk": 120, "fields": {"app_label": "oauth2_provider", "model": "application"}}, {"model": "contenttypes.contenttype", "pk": 121, "fields": {"app_label": "oauth2_provider", "model": "grant"}}, {"model": "contenttypes.contenttype", "pk": 122, "fields": {"app_label": "oauth2_provider", "model": "accesstoken"}}, {"model": "contenttypes.contenttype", "pk": 123, "fields": {"app_label": "oauth2_provider", "model": "refreshtoken"}}, {"model": "contenttypes.contenttype", "pk": 124, "fields": {"app_label": "oauth_dispatch", "model": "restrictedapplication"}}, {"model": "contenttypes.contenttype", "pk": 125, "fields": {"app_label": "third_party_auth", "model": "oauth2providerconfig"}}, {"model": "contenttypes.contenttype", "pk": 126, "fields": {"app_label": "third_party_auth", "model": "samlproviderconfig"}}, {"model": "contenttypes.contenttype", "pk": 127, "fields": {"app_label": "third_party_auth", "model": "samlconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 128, "fields": {"app_label": "third_party_auth", "model": "samlproviderdata"}}, {"model": "contenttypes.contenttype", "pk": 129, "fields": {"app_label": "third_party_auth", "model": "ltiproviderconfig"}}, {"model": "contenttypes.contenttype", "pk": 130, "fields": {"app_label": "third_party_auth", "model": "providerapipermissions"}}, {"model": "contenttypes.contenttype", "pk": 131, "fields": {"app_label": "oauth_provider", "model": "nonce"}}, {"model": "contenttypes.contenttype", "pk": 132, "fields": {"app_label": "oauth_provider", "model": "scope"}}, {"model": "contenttypes.contenttype", "pk": 133, "fields": {"app_label": "oauth_provider", "model": "consumer"}}, {"model": "contenttypes.contenttype", "pk": 134, "fields": {"app_label": "oauth_provider", "model": "token"}}, {"model": "contenttypes.contenttype", "pk": 135, "fields": {"app_label": "oauth_provider", "model": "resource"}}, {"model": "contenttypes.contenttype", "pk": 136, "fields": {"app_label": "wiki", "model": "article"}}, {"model": "contenttypes.contenttype", "pk": 137, "fields": {"app_label": "wiki", "model": "articleforobject"}}, {"model": "contenttypes.contenttype", "pk": 138, "fields": {"app_label": "wiki", "model": "articlerevision"}}, {"model": "contenttypes.contenttype", "pk": 139, "fields": {"app_label": "wiki", "model": "articleplugin"}}, {"model": "contenttypes.contenttype", "pk": 140, "fields": {"app_label": "wiki", "model": "reusableplugin"}}, {"model": "contenttypes.contenttype", "pk": 141, "fields": {"app_label": "wiki", "model": "simpleplugin"}}, {"model": "contenttypes.contenttype", "pk": 142, "fields": {"app_label": "wiki", "model": "revisionplugin"}}, {"model": "contenttypes.contenttype", "pk": 143, "fields": {"app_label": "wiki", "model": "revisionpluginrevision"}}, {"model": "contenttypes.contenttype", "pk": 144, "fields": {"app_label": "wiki", "model": "urlpath"}}, {"model": "contenttypes.contenttype", "pk": 145, "fields": {"app_label": "django_notify", "model": "notificationtype"}}, {"model": "contenttypes.contenttype", "pk": 146, "fields": {"app_label": "django_notify", "model": "settings"}}, {"model": "contenttypes.contenttype", "pk": 147, "fields": {"app_label": "django_notify", "model": "subscription"}}, {"model": "contenttypes.contenttype", "pk": 148, "fields": {"app_label": "django_notify", "model": "notification"}}, {"model": "contenttypes.contenttype", "pk": 149, "fields": {"app_label": "admin", "model": "logentry"}}, {"model": "contenttypes.contenttype", "pk": 150, "fields": {"app_label": "django_comment_common", "model": "role"}}, {"model": "contenttypes.contenttype", "pk": 151, "fields": {"app_label": "django_comment_common", "model": "permission"}}, {"model": "contenttypes.contenttype", "pk": 152, "fields": {"app_label": "django_comment_common", "model": "forumsconfig"}}, {"model": "contenttypes.contenttype", "pk": 153, "fields": {"app_label": "django_comment_common", "model": "coursediscussionsettings"}}, {"model": "contenttypes.contenttype", "pk": 154, "fields": {"app_label": "notes", "model": "note"}}, {"model": "contenttypes.contenttype", "pk": 155, "fields": {"app_label": "splash", "model": "splashconfig"}}, {"model": "contenttypes.contenttype", "pk": 156, "fields": {"app_label": "user_api", "model": "userpreference"}}, {"model": "contenttypes.contenttype", "pk": 157, "fields": {"app_label": "user_api", "model": "usercoursetag"}}, {"model": "contenttypes.contenttype", "pk": 158, "fields": {"app_label": "user_api", "model": "userorgtag"}}, {"model": "contenttypes.contenttype", "pk": 159, "fields": {"app_label": "shoppingcart", "model": "order"}}, {"model": "contenttypes.contenttype", "pk": 160, "fields": {"app_label": "shoppingcart", "model": "orderitem"}}, {"model": "contenttypes.contenttype", "pk": 161, "fields": {"app_label": "shoppingcart", "model": "invoice"}}, {"model": "contenttypes.contenttype", "pk": 162, "fields": {"app_label": "shoppingcart", "model": "invoicetransaction"}}, {"model": "contenttypes.contenttype", "pk": 163, "fields": {"app_label": "shoppingcart", "model": "invoiceitem"}}, {"model": "contenttypes.contenttype", "pk": 164, "fields": {"app_label": "shoppingcart", "model": "courseregistrationcodeinvoiceitem"}}, {"model": "contenttypes.contenttype", "pk": 165, "fields": {"app_label": "shoppingcart", "model": "invoicehistory"}}, {"model": "contenttypes.contenttype", "pk": 166, "fields": {"app_label": "shoppingcart", "model": "courseregistrationcode"}}, {"model": "contenttypes.contenttype", "pk": 167, "fields": {"app_label": "shoppingcart", "model": "registrationcoderedemption"}}, {"model": "contenttypes.contenttype", "pk": 168, "fields": {"app_label": "shoppingcart", "model": "coupon"}}, {"model": "contenttypes.contenttype", "pk": 169, "fields": {"app_label": "shoppingcart", "model": "couponredemption"}}, {"model": "contenttypes.contenttype", "pk": 170, "fields": {"app_label": "shoppingcart", "model": "paidcourseregistration"}}, {"model": "contenttypes.contenttype", "pk": 171, "fields": {"app_label": "shoppingcart", "model": "courseregcodeitem"}}, {"model": "contenttypes.contenttype", "pk": 172, "fields": {"app_label": "shoppingcart", "model": "courseregcodeitemannotation"}}, {"model": "contenttypes.contenttype", "pk": 173, "fields": {"app_label": "shoppingcart", "model": "paidcourseregistrationannotation"}}, {"model": "contenttypes.contenttype", "pk": 174, "fields": {"app_label": "shoppingcart", "model": "certificateitem"}}, {"model": "contenttypes.contenttype", "pk": 175, "fields": {"app_label": "shoppingcart", "model": "donationconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 176, "fields": {"app_label": "shoppingcart", "model": "donation"}}, {"model": "contenttypes.contenttype", "pk": 177, "fields": {"app_label": "course_modes", "model": "coursemode"}}, {"model": "contenttypes.contenttype", "pk": 178, "fields": {"app_label": "course_modes", "model": "coursemodesarchive"}}, {"model": "contenttypes.contenttype", "pk": 179, "fields": {"app_label": "course_modes", "model": "coursemodeexpirationconfig"}}, {"model": "contenttypes.contenttype", "pk": 180, "fields": {"app_label": "entitlements", "model": "courseentitlement"}}, {"model": "contenttypes.contenttype", "pk": 181, "fields": {"app_label": "verify_student", "model": "softwaresecurephotoverification"}}, {"model": "contenttypes.contenttype", "pk": 182, "fields": {"app_label": "verify_student", "model": "verificationdeadline"}}, {"model": "contenttypes.contenttype", "pk": 183, "fields": {"app_label": "verify_student", "model": "verificationcheckpoint"}}, {"model": "contenttypes.contenttype", "pk": 184, "fields": {"app_label": "verify_student", "model": "verificationstatus"}}, {"model": "contenttypes.contenttype", "pk": 185, "fields": {"app_label": "verify_student", "model": "incoursereverificationconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 186, "fields": {"app_label": "verify_student", "model": "icrvstatusemailsconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 187, "fields": {"app_label": "verify_student", "model": "skippedreverification"}}, {"model": "contenttypes.contenttype", "pk": 188, "fields": {"app_label": "dark_lang", "model": "darklangconfig"}}, {"model": "contenttypes.contenttype", "pk": 189, "fields": {"app_label": "microsite_configuration", "model": "microsite"}}, {"model": "contenttypes.contenttype", "pk": 190, "fields": {"app_label": "microsite_configuration", "model": "micrositehistory"}}, {"model": "contenttypes.contenttype", "pk": 191, "fields": {"app_label": "microsite_configuration", "model": "micrositeorganizationmapping"}}, {"model": "contenttypes.contenttype", "pk": 192, "fields": {"app_label": "microsite_configuration", "model": "micrositetemplate"}}, {"model": "contenttypes.contenttype", "pk": 193, "fields": {"app_label": "rss_proxy", "model": "whitelistedrssurl"}}, {"model": "contenttypes.contenttype", "pk": 194, "fields": {"app_label": "embargo", "model": "embargoedcourse"}}, {"model": "contenttypes.contenttype", "pk": 195, "fields": {"app_label": "embargo", "model": "embargoedstate"}}, {"model": "contenttypes.contenttype", "pk": 196, "fields": {"app_label": "embargo", "model": "restrictedcourse"}}, {"model": "contenttypes.contenttype", "pk": 197, "fields": {"app_label": "embargo", "model": "country"}}, {"model": "contenttypes.contenttype", "pk": 198, "fields": {"app_label": "embargo", "model": "countryaccessrule"}}, {"model": "contenttypes.contenttype", "pk": 199, "fields": {"app_label": "embargo", "model": "courseaccessrulehistory"}}, {"model": "contenttypes.contenttype", "pk": 200, "fields": {"app_label": "embargo", "model": "ipfilter"}}, {"model": "contenttypes.contenttype", "pk": 201, "fields": {"app_label": "course_action_state", "model": "coursererunstate"}}, {"model": "contenttypes.contenttype", "pk": 202, "fields": {"app_label": "mobile_api", "model": "mobileapiconfig"}}, {"model": "contenttypes.contenttype", "pk": 203, "fields": {"app_label": "mobile_api", "model": "appversionconfig"}}, {"model": "contenttypes.contenttype", "pk": 204, "fields": {"app_label": "mobile_api", "model": "ignoremobileavailableflagconfig"}}, {"model": "contenttypes.contenttype", "pk": 205, "fields": {"app_label": "social_django", "model": "usersocialauth"}}, {"model": "contenttypes.contenttype", "pk": 206, "fields": {"app_label": "social_django", "model": "nonce"}}, {"model": "contenttypes.contenttype", "pk": 207, "fields": {"app_label": "social_django", "model": "association"}}, {"model": "contenttypes.contenttype", "pk": 208, "fields": {"app_label": "social_django", "model": "code"}}, {"model": "contenttypes.contenttype", "pk": 209, "fields": {"app_label": "social_django", "model": "partial"}}, {"model": "contenttypes.contenttype", "pk": 210, "fields": {"app_label": "survey", "model": "surveyform"}}, {"model": "contenttypes.contenttype", "pk": 211, "fields": {"app_label": "survey", "model": "surveyanswer"}}, {"model": "contenttypes.contenttype", "pk": 212, "fields": {"app_label": "lms_xblock", "model": "xblockasidesconfig"}}, {"model": "contenttypes.contenttype", "pk": 213, "fields": {"app_label": "problem_builder", "model": "answer"}}, {"model": "contenttypes.contenttype", "pk": 214, "fields": {"app_label": "problem_builder", "model": "share"}}, {"model": "contenttypes.contenttype", "pk": 215, "fields": {"app_label": "submissions", "model": "studentitem"}}, {"model": "contenttypes.contenttype", "pk": 216, "fields": {"app_label": "submissions", "model": "submission"}}, {"model": "contenttypes.contenttype", "pk": 217, "fields": {"app_label": "submissions", "model": "score"}}, {"model": "contenttypes.contenttype", "pk": 218, "fields": {"app_label": "submissions", "model": "scoresummary"}}, {"model": "contenttypes.contenttype", "pk": 219, "fields": {"app_label": "submissions", "model": "scoreannotation"}}, {"model": "contenttypes.contenttype", "pk": 220, "fields": {"app_label": "assessment", "model": "rubric"}}, {"model": "contenttypes.contenttype", "pk": 221, "fields": {"app_label": "assessment", "model": "criterion"}}, {"model": "contenttypes.contenttype", "pk": 222, "fields": {"app_label": "assessment", "model": "criterionoption"}}, {"model": "contenttypes.contenttype", "pk": 223, "fields": {"app_label": "assessment", "model": "assessment"}}, {"model": "contenttypes.contenttype", "pk": 224, "fields": {"app_label": "assessment", "model": "assessmentpart"}}, {"model": "contenttypes.contenttype", "pk": 225, "fields": {"app_label": "assessment", "model": "assessmentfeedbackoption"}}, {"model": "contenttypes.contenttype", "pk": 226, "fields": {"app_label": "assessment", "model": "assessmentfeedback"}}, {"model": "contenttypes.contenttype", "pk": 227, "fields": {"app_label": "assessment", "model": "peerworkflow"}}, {"model": "contenttypes.contenttype", "pk": 228, "fields": {"app_label": "assessment", "model": "peerworkflowitem"}}, {"model": "contenttypes.contenttype", "pk": 229, "fields": {"app_label": "assessment", "model": "trainingexample"}}, {"model": "contenttypes.contenttype", "pk": 230, "fields": {"app_label": "assessment", "model": "studenttrainingworkflow"}}, {"model": "contenttypes.contenttype", "pk": 231, "fields": {"app_label": "assessment", "model": "studenttrainingworkflowitem"}}, {"model": "contenttypes.contenttype", "pk": 232, "fields": {"app_label": "assessment", "model": "staffworkflow"}}, {"model": "contenttypes.contenttype", "pk": 233, "fields": {"app_label": "workflow", "model": "assessmentworkflow"}}, {"model": "contenttypes.contenttype", "pk": 234, "fields": {"app_label": "workflow", "model": "assessmentworkflowstep"}}, {"model": "contenttypes.contenttype", "pk": 235, "fields": {"app_label": "workflow", "model": "assessmentworkflowcancellation"}}, {"model": "contenttypes.contenttype", "pk": 236, "fields": {"app_label": "edxval", "model": "profile"}}, {"model": "contenttypes.contenttype", "pk": 237, "fields": {"app_label": "edxval", "model": "video"}}, {"model": "contenttypes.contenttype", "pk": 238, "fields": {"app_label": "edxval", "model": "coursevideo"}}, {"model": "contenttypes.contenttype", "pk": 239, "fields": {"app_label": "edxval", "model": "encodedvideo"}}, {"model": "contenttypes.contenttype", "pk": 240, "fields": {"app_label": "edxval", "model": "videoimage"}}, {"model": "contenttypes.contenttype", "pk": 241, "fields": {"app_label": "edxval", "model": "videotranscript"}}, {"model": "contenttypes.contenttype", "pk": 242, "fields": {"app_label": "edxval", "model": "transcriptpreference"}}, {"model": "contenttypes.contenttype", "pk": 243, "fields": {"app_label": "edxval", "model": "thirdpartytranscriptcredentialsstate"}}, {"model": "contenttypes.contenttype", "pk": 244, "fields": {"app_label": "course_overviews", "model": "courseoverview"}}, {"model": "contenttypes.contenttype", "pk": 245, "fields": {"app_label": "course_overviews", "model": "courseoverviewtab"}}, {"model": "contenttypes.contenttype", "pk": 246, "fields": {"app_label": "course_overviews", "model": "courseoverviewimageset"}}, {"model": "contenttypes.contenttype", "pk": 247, "fields": {"app_label": "course_overviews", "model": "courseoverviewimageconfig"}}, {"model": "contenttypes.contenttype", "pk": 248, "fields": {"app_label": "course_structures", "model": "coursestructure"}}, {"model": "contenttypes.contenttype", "pk": 249, "fields": {"app_label": "block_structure", "model": "blockstructureconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 250, "fields": {"app_label": "block_structure", "model": "blockstructuremodel"}}, {"model": "contenttypes.contenttype", "pk": 251, "fields": {"app_label": "cors_csrf", "model": "xdomainproxyconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 252, "fields": {"app_label": "commerce", "model": "commerceconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 253, "fields": {"app_label": "credit", "model": "creditprovider"}}, {"model": "contenttypes.contenttype", "pk": 254, "fields": {"app_label": "credit", "model": "creditcourse"}}, {"model": "contenttypes.contenttype", "pk": 255, "fields": {"app_label": "credit", "model": "creditrequirement"}}, {"model": "contenttypes.contenttype", "pk": 256, "fields": {"app_label": "credit", "model": "creditrequirementstatus"}}, {"model": "contenttypes.contenttype", "pk": 257, "fields": {"app_label": "credit", "model": "crediteligibility"}}, {"model": "contenttypes.contenttype", "pk": 258, "fields": {"app_label": "credit", "model": "creditrequest"}}, {"model": "contenttypes.contenttype", "pk": 259, "fields": {"app_label": "credit", "model": "creditconfig"}}, {"model": "contenttypes.contenttype", "pk": 260, "fields": {"app_label": "teams", "model": "courseteam"}}, {"model": "contenttypes.contenttype", "pk": 261, "fields": {"app_label": "teams", "model": "courseteammembership"}}, {"model": "contenttypes.contenttype", "pk": 262, "fields": {"app_label": "xblock_django", "model": "xblockconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 263, "fields": {"app_label": "xblock_django", "model": "xblockstudioconfigurationflag"}}, {"model": "contenttypes.contenttype", "pk": 264, "fields": {"app_label": "xblock_django", "model": "xblockstudioconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 265, "fields": {"app_label": "programs", "model": "programsapiconfig"}}, {"model": "contenttypes.contenttype", "pk": 266, "fields": {"app_label": "catalog", "model": "catalogintegration"}}, {"model": "contenttypes.contenttype", "pk": 267, "fields": {"app_label": "self_paced", "model": "selfpacedconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 268, "fields": {"app_label": "thumbnail", "model": "kvstore"}}, {"model": "contenttypes.contenttype", "pk": 269, "fields": {"app_label": "credentials", "model": "credentialsapiconfig"}}, {"model": "contenttypes.contenttype", "pk": 270, "fields": {"app_label": "milestones", "model": "milestone"}}, {"model": "contenttypes.contenttype", "pk": 271, "fields": {"app_label": "milestones", "model": "milestonerelationshiptype"}}, {"model": "contenttypes.contenttype", "pk": 272, "fields": {"app_label": "milestones", "model": "coursemilestone"}}, {"model": "contenttypes.contenttype", "pk": 273, "fields": {"app_label": "milestones", "model": "coursecontentmilestone"}}, {"model": "contenttypes.contenttype", "pk": 274, "fields": {"app_label": "milestones", "model": "usermilestone"}}, {"model": "contenttypes.contenttype", "pk": 275, "fields": {"app_label": "api_admin", "model": "apiaccessconfig"}}, {"model": "contenttypes.contenttype", "pk": 276, "fields": {"app_label": "api_admin", "model": "catalog"}}, {"model": "contenttypes.contenttype", "pk": 277, "fields": {"app_label": "verified_track_content", "model": "verifiedtrackcohortedcourse"}}, {"model": "contenttypes.contenttype", "pk": 278, "fields": {"app_label": "verified_track_content", "model": "migrateverifiedtrackcohortssetting"}}, {"model": "contenttypes.contenttype", "pk": 279, "fields": {"app_label": "badges", "model": "badgeclass"}}, {"model": "contenttypes.contenttype", "pk": 280, "fields": {"app_label": "badges", "model": "badgeassertion"}}, {"model": "contenttypes.contenttype", "pk": 281, "fields": {"app_label": "badges", "model": "coursecompleteimageconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 282, "fields": {"app_label": "badges", "model": "courseeventbadgesconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 283, "fields": {"app_label": "email_marketing", "model": "emailmarketingconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 284, "fields": {"app_label": "celery_utils", "model": "failedtask"}}, {"model": "contenttypes.contenttype", "pk": 285, "fields": {"app_label": "celery_utils", "model": "chorddata"}}, {"model": "contenttypes.contenttype", "pk": 286, "fields": {"app_label": "crawlers", "model": "crawlersconfig"}}, {"model": "contenttypes.contenttype", "pk": 287, "fields": {"app_label": "waffle_utils", "model": "waffleflagcourseoverridemodel"}}, {"model": "contenttypes.contenttype", "pk": 288, "fields": {"app_label": "schedules", "model": "schedule"}}, {"model": "contenttypes.contenttype", "pk": 289, "fields": {"app_label": "schedules", "model": "scheduleconfig"}}, {"model": "contenttypes.contenttype", "pk": 290, "fields": {"app_label": "schedules", "model": "scheduleexperience"}}, {"model": "contenttypes.contenttype", "pk": 291, "fields": {"app_label": "course_goals", "model": "coursegoal"}}, {"model": "contenttypes.contenttype", "pk": 292, "fields": {"app_label": "completion", "model": "blockcompletion"}}, {"model": "contenttypes.contenttype", "pk": 293, "fields": {"app_label": "experiments", "model": "experimentdata"}}, {"model": "contenttypes.contenttype", "pk": 294, "fields": {"app_label": "experiments", "model": "experimentkeyvalue"}}, {"model": "contenttypes.contenttype", "pk": 295, "fields": {"app_label": "edx_proctoring", "model": "proctoredexam"}}, {"model": "contenttypes.contenttype", "pk": 296, "fields": {"app_label": "edx_proctoring", "model": "proctoredexamreviewpolicy"}}, {"model": "contenttypes.contenttype", "pk": 297, "fields": {"app_label": "edx_proctoring", "model": "proctoredexamreviewpolicyhistory"}}, {"model": "contenttypes.contenttype", "pk": 298, "fields": {"app_label": "edx_proctoring", "model": "proctoredexamstudentattempt"}}, {"model": "contenttypes.contenttype", "pk": 299, "fields": {"app_label": "edx_proctoring", "model": "proctoredexamstudentattempthistory"}}, {"model": "contenttypes.contenttype", "pk": 300, "fields": {"app_label": "edx_proctoring", "model": "proctoredexamstudentallowance"}}, {"model": "contenttypes.contenttype", "pk": 301, "fields": {"app_label": "edx_proctoring", "model": "proctoredexamstudentallowancehistory"}}, {"model": "contenttypes.contenttype", "pk": 302, "fields": {"app_label": "edx_proctoring", "model": "proctoredexamsoftwaresecurereview"}}, {"model": "contenttypes.contenttype", "pk": 303, "fields": {"app_label": "edx_proctoring", "model": "proctoredexamsoftwaresecurereviewhistory"}}, {"model": "contenttypes.contenttype", "pk": 304, "fields": {"app_label": "edx_proctoring", "model": "proctoredexamsoftwaresecurecomment"}}, {"model": "contenttypes.contenttype", "pk": 305, "fields": {"app_label": "organizations", "model": "organization"}}, {"model": "contenttypes.contenttype", "pk": 306, "fields": {"app_label": "organizations", "model": "organizationcourse"}}, {"model": "contenttypes.contenttype", "pk": 307, "fields": {"app_label": "enterprise", "model": "historicalenterprisecustomer"}}, {"model": "contenttypes.contenttype", "pk": 308, "fields": {"app_label": "enterprise", "model": "enterprisecustomer"}}, {"model": "contenttypes.contenttype", "pk": 309, "fields": {"app_label": "enterprise", "model": "enterprisecustomeruser"}}, {"model": "contenttypes.contenttype", "pk": 310, "fields": {"app_label": "enterprise", "model": "pendingenterprisecustomeruser"}}, {"model": "contenttypes.contenttype", "pk": 311, "fields": {"app_label": "enterprise", "model": "pendingenrollment"}}, {"model": "contenttypes.contenttype", "pk": 312, "fields": {"app_label": "enterprise", "model": "enterprisecustomerbrandingconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 313, "fields": {"app_label": "enterprise", "model": "enterprisecustomeridentityprovider"}}, {"model": "contenttypes.contenttype", "pk": 314, "fields": {"app_label": "enterprise", "model": "historicalenterprisecustomerentitlement"}}, {"model": "contenttypes.contenttype", "pk": 315, "fields": {"app_label": "enterprise", "model": "enterprisecustomerentitlement"}}, {"model": "contenttypes.contenttype", "pk": 316, "fields": {"app_label": "enterprise", "model": "historicalenterprisecourseenrollment"}}, {"model": "contenttypes.contenttype", "pk": 317, "fields": {"app_label": "enterprise", "model": "enterprisecourseenrollment"}}, {"model": "contenttypes.contenttype", "pk": 318, "fields": {"app_label": "enterprise", "model": "historicalenterprisecustomercatalog"}}, {"model": "contenttypes.contenttype", "pk": 319, "fields": {"app_label": "enterprise", "model": "enterprisecustomercatalog"}}, {"model": "contenttypes.contenttype", "pk": 320, "fields": {"app_label": "enterprise", "model": "historicalenrollmentnotificationemailtemplate"}}, {"model": "contenttypes.contenttype", "pk": 321, "fields": {"app_label": "enterprise", "model": "enrollmentnotificationemailtemplate"}}, {"model": "contenttypes.contenttype", "pk": 322, "fields": {"app_label": "enterprise", "model": "enterprisecustomerreportingconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 323, "fields": {"app_label": "consent", "model": "historicaldatasharingconsent"}}, {"model": "contenttypes.contenttype", "pk": 324, "fields": {"app_label": "consent", "model": "datasharingconsent"}}, {"model": "contenttypes.contenttype", "pk": 325, "fields": {"app_label": "integrated_channel", "model": "learnerdatatransmissionaudit"}}, {"model": "contenttypes.contenttype", "pk": 326, "fields": {"app_label": "integrated_channel", "model": "catalogtransmissionaudit"}}, {"model": "contenttypes.contenttype", "pk": 327, "fields": {"app_label": "degreed", "model": "degreedglobalconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 328, "fields": {"app_label": "degreed", "model": "historicaldegreedenterprisecustomerconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 329, "fields": {"app_label": "degreed", "model": "degreedenterprisecustomerconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 330, "fields": {"app_label": "degreed", "model": "degreedlearnerdatatransmissionaudit"}}, {"model": "contenttypes.contenttype", "pk": 331, "fields": {"app_label": "sap_success_factors", "model": "sapsuccessfactorsglobalconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 332, "fields": {"app_label": "sap_success_factors", "model": "historicalsapsuccessfactorsenterprisecustomerconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 333, "fields": {"app_label": "sap_success_factors", "model": "sapsuccessfactorsenterprisecustomerconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 334, "fields": {"app_label": "sap_success_factors", "model": "sapsuccessfactorslearnerdatatransmissionaudit"}}, {"model": "contenttypes.contenttype", "pk": 335, "fields": {"app_label": "ccx", "model": "customcourseforedx"}}, {"model": "contenttypes.contenttype", "pk": 336, "fields": {"app_label": "ccx", "model": "ccxfieldoverride"}}, {"model": "contenttypes.contenttype", "pk": 337, "fields": {"app_label": "ccxcon", "model": "ccxcon"}}, {"model": "contenttypes.contenttype", "pk": 338, "fields": {"app_label": "coursewarehistoryextended", "model": "studentmodulehistoryextended"}}, {"model": "contenttypes.contenttype", "pk": 339, "fields": {"app_label": "contentstore", "model": "videouploadconfig"}}, {"model": "contenttypes.contenttype", "pk": 340, "fields": {"app_label": "contentstore", "model": "pushnotificationconfig"}}, {"model": "contenttypes.contenttype", "pk": 341, "fields": {"app_label": "contentstore", "model": "newassetspageflag"}}, {"model": "contenttypes.contenttype", "pk": 342, "fields": {"app_label": "contentstore", "model": "coursenewassetspageflag"}}, {"model": "contenttypes.contenttype", "pk": 343, "fields": {"app_label": "course_creators", "model": "coursecreator"}}, {"model": "contenttypes.contenttype", "pk": 344, "fields": {"app_label": "xblock_config", "model": "studioconfig"}}, {"model": "contenttypes.contenttype", "pk": 345, "fields": {"app_label": "xblock_config", "model": "courseeditltifieldsenabledflag"}}, {"model": "contenttypes.contenttype", "pk": 346, "fields": {"app_label": "tagging", "model": "tagcategories"}}, {"model": "contenttypes.contenttype", "pk": 347, "fields": {"app_label": "tagging", "model": "tagavailablevalues"}}, {"model": "contenttypes.contenttype", "pk": 348, "fields": {"app_label": "user_tasks", "model": "usertaskstatus"}}, {"model": "contenttypes.contenttype", "pk": 349, "fields": {"app_label": "user_tasks", "model": "usertaskartifact"}}, {"model": "contenttypes.contenttype", "pk": 350, "fields": {"app_label": "entitlements", "model": "courseentitlementpolicy"}}, {"model": "contenttypes.contenttype", "pk": 351, "fields": {"app_label": "entitlements", "model": "courseentitlementsupportdetail"}}, {"model": "contenttypes.contenttype", "pk": 352, "fields": {"app_label": "integrated_channel", "model": "contentmetadataitemtransmission"}}, {"model": "contenttypes.contenttype", "pk": 353, "fields": {"app_label": "video_config", "model": "transcriptmigrationsetting"}}, {"model": "contenttypes.contenttype", "pk": 354, "fields": {"app_label": "verify_student", "model": "ssoverification"}}, {"model": "contenttypes.contenttype", "pk": 355, "fields": {"app_label": "user_api", "model": "userretirementstatus"}}, {"model": "contenttypes.contenttype", "pk": 356, "fields": {"app_label": "user_api", "model": "retirementstate"}}, {"model": "contenttypes.contenttype", "pk": 357, "fields": {"app_label": "consent", "model": "datasharingconsenttextoverrides"}}, {"model": "contenttypes.contenttype", "pk": 358, "fields": {"app_label": "user_api", "model": "userretirementrequest"}}, {"model": "contenttypes.contenttype", "pk": 359, "fields": {"app_label": "django_comment_common", "model": "discussionsidmapping"}}, {"model": "contenttypes.contenttype", "pk": 360, "fields": {"app_label": "oauth_dispatch", "model": "scopedapplication"}}, {"model": "contenttypes.contenttype", "pk": 361, "fields": {"app_label": "oauth_dispatch", "model": "scopedapplicationorganization"}}, {"model": "contenttypes.contenttype", "pk": 362, "fields": {"app_label": "user_api", "model": "userretirementpartnerreportingstatus"}}, {"model": "contenttypes.contenttype", "pk": 363, "fields": {"app_label": "verify_student", "model": "manualverification"}}, {"model": "contenttypes.contenttype", "pk": 364, "fields": {"app_label": "oauth_dispatch", "model": "applicationorganization"}}, {"model": "contenttypes.contenttype", "pk": 365, "fields": {"app_label": "oauth_dispatch", "model": "applicationaccess"}}, {"model": "contenttypes.contenttype", "pk": 366, "fields": {"app_label": "video_config", "model": "migrationenqueuedcourse"}}, {"model": "contenttypes.contenttype", "pk": 367, "fields": {"app_label": "xapi", "model": "xapilrsconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 368, "fields": {"app_label": "credentials", "model": "notifycredentialsconfig"}}, {"model": "contenttypes.contenttype", "pk": 369, "fields": {"app_label": "video_config", "model": "updatedcoursevideos"}}, {"model": "contenttypes.contenttype", "pk": 370, "fields": {"app_label": "video_config", "model": "videothumbnailsetting"}}, {"model": "contenttypes.contenttype", "pk": 371, "fields": {"app_label": "course_duration_limits", "model": "coursedurationlimitconfig"}}, {"model": "contenttypes.contenttype", "pk": 372, "fields": {"app_label": "content_type_gating", "model": "contenttypegatingconfig"}}, {"model": "contenttypes.contenttype", "pk": 373, "fields": {"app_label": "grades", "model": "persistentsubsectiongradeoverridehistory"}}, {"model": "contenttypes.contenttype", "pk": 374, "fields": {"app_label": "student", "model": "accountrecovery"}}, {"model": "contenttypes.contenttype", "pk": 375, "fields": {"app_label": "enterprise", "model": "enterprisecustomertype"}}, {"model": "contenttypes.contenttype", "pk": 376, "fields": {"app_label": "student", "model": "pendingsecondaryemailchange"}}, {"model": "contenttypes.contenttype", "pk": 377, "fields": {"app_label": "lti_provider", "model": "lticonsumer"}}, {"model": "contenttypes.contenttype", "pk": 378, "fields": {"app_label": "lti_provider", "model": "gradedassignment"}}, {"model": "contenttypes.contenttype", "pk": 379, "fields": {"app_label": "lti_provider", "model": "ltiuser"}}, {"model": "contenttypes.contenttype", "pk": 380, "fields": {"app_label": "lti_provider", "model": "outcomeservice"}}, {"model": "contenttypes.contenttype", "pk": 381, "fields": {"app_label": "enterprise", "model": "systemwideenterpriserole"}}, {"model": "contenttypes.contenttype", "pk": 382, "fields": {"app_label": "enterprise", "model": "systemwideenterpriseuserroleassignment"}}, {"model": "contenttypes.contenttype", "pk": 383, "fields": {"app_label": "announcements", "model": "announcement"}}, {"model": "contenttypes.contenttype", "pk": 753, "fields": {"app_label": "enterprise", "model": "enterprisefeatureuserroleassignment"}}, {"model": "contenttypes.contenttype", "pk": 754, "fields": {"app_label": "enterprise", "model": "enterprisefeaturerole"}}, {"model": "contenttypes.contenttype", "pk": 755, "fields": {"app_label": "program_enrollments", "model": "programenrollment"}}, {"model": "contenttypes.contenttype", "pk": 756, "fields": {"app_label": "program_enrollments", "model": "historicalprogramenrollment"}}, {"model": "contenttypes.contenttype", "pk": 757, "fields": {"app_label": "program_enrollments", "model": "programcourseenrollment"}}, {"model": "contenttypes.contenttype", "pk": 758, "fields": {"app_label": "program_enrollments", "model": "historicalprogramcourseenrollment"}}, {"model": "contenttypes.contenttype", "pk": 759, "fields": {"app_label": "edx_when", "model": "contentdate"}}, {"model": "contenttypes.contenttype", "pk": 760, "fields": {"app_label": "edx_when", "model": "userdate"}}, {"model": "contenttypes.contenttype", "pk": 761, "fields": {"app_label": "edx_when", "model": "datepolicy"}}, {"model": "contenttypes.contenttype", "pk": 762, "fields": {"app_label": "student", "model": "historicalcourseenrollment"}}, {"model": "contenttypes.contenttype", "pk": 763, "fields": {"app_label": "cornerstone", "model": "cornerstoneglobalconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 764, "fields": {"app_label": "cornerstone", "model": "historicalcornerstoneenterprisecustomerconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 765, "fields": {"app_label": "cornerstone", "model": "cornerstonelearnerdatatransmissionaudit"}}, {"model": "contenttypes.contenttype", "pk": 766, "fields": {"app_label": "cornerstone", "model": "cornerstoneenterprisecustomerconfiguration"}}, {"model": "contenttypes.contenttype", "pk": 767, "fields": {"app_label": "discounts", "model": "discountrestrictionconfig"}}, {"model": "contenttypes.contenttype", "pk": 768, "fields": {"app_label": "entitlements", "model": "historicalcourseentitlement"}}, {"model": "contenttypes.contenttype", "pk": 769, "fields": {"app_label": "organizations", "model": "historicalorganization"}}, {"model": "contenttypes.contenttype", "pk": 770, "fields": {"app_label": "grades", "model": "historicalpersistentsubsectiongradeoverride"}}, {"model": "contenttypes.contenttype", "pk": 771, "fields": {"app_label": "super_csv", "model": "csvoperation"}}, {"model": "contenttypes.contenttype", "pk": 772, "fields": {"app_label": "bulk_grades", "model": "scoreoverrider"}}, {"model": "contenttypes.contenttype", "pk": 773, "fields": {"app_label": "course_modes", "model": "historicalcoursemode"}}, {"model": "contenttypes.contenttype", "pk": 774, "fields": {"app_label": "course_overviews", "model": "historicalcourseoverview"}}, {"model": "contenttypes.contenttype", "pk": 775, "fields": {"app_label": "system_wide_roles", "model": "systemwiderole"}}, {"model": "contenttypes.contenttype", "pk": 776, "fields": {"app_label": "system_wide_roles", "model": "systemwideroleassignment"}}, {"model": "contenttypes.contenttype", "pk": 777, "fields": {"app_label": "enterprise", "model": "enterprisecatalogquery"}}, {"model": "contenttypes.contenttype", "pk": 778, "fields": {"app_label": "enterprise", "model": "historicalpendingenrollment"}}, {"model": "contenttypes.contenttype", "pk": 779, "fields": {"app_label": "enterprise", "model": "historicalpendingenterprisecustomeruser"}}, {"model": "contenttypes.contenttype", "pk": 780, "fields": {"app_label": "xapi", "model": "xapilearnerdatatransmissionaudit"}}, {"model": "contenttypes.contenttype", "pk": 781, "fields": {"app_label": "video_config", "model": "courseyoutubeblockedflag"}}, {"model": "contenttypes.contenttype", "pk": 782, "fields": {"app_label": "content_libraries", "model": "contentlibrary"}}, {"model": "contenttypes.contenttype", "pk": 783, "fields": {"app_label": "content_libraries", "model": "contentlibrarypermission"}}, {"model": "contenttypes.contenttype", "pk": 784, "fields": {"app_label": "course_overviews", "model": "simulatecoursepublishconfig"}}, {"model": "sites.site", "pk": 1, "fields": {"domain": "example.com", "name": "example.com"}}, {"model": "bulk_email.courseemailtemplate", "pk": 1, "fields": {"html_template": "
Update from {course_title}