diff --git a/common/djangoapps/course_modes/models.py b/common/djangoapps/course_modes/models.py
index 7e80d40095..c8c07eae0a 100644
--- a/common/djangoapps/course_modes/models.py
+++ b/common/djangoapps/course_modes/models.py
@@ -592,18 +592,6 @@ class CourseMode(models.Model):
modes = cls.modes_for_course(course_id)
return min(mode.min_price for mode in modes if mode.currency.lower() == currency.lower())
- @classmethod
- def is_eligible_for_certificate(cls, mode_slug):
- """
- Returns whether or not the given mode_slug is eligible for a
- certificate. Currently all modes other than 'audit' grant a
- certificate. Note that audit enrollments which existed prior
- to December 2015 *were* given certificates, so there will be
- GeneratedCertificate records with mode='audit' and
- eligible_for_certificate=True.
- """
- return mode_slug != cls.AUDIT
-
def to_tuple(self):
"""
Takes a mode model and turns it into a model named tuple.
diff --git a/common/djangoapps/course_modes/tests/test_models.py b/common/djangoapps/course_modes/tests/test_models.py
index 0d525128dc..c6406e75ed 100644
--- a/common/djangoapps/course_modes/tests/test_models.py
+++ b/common/djangoapps/course_modes/tests/test_models.py
@@ -430,16 +430,3 @@ class CourseModeModelTest(TestCase):
verified_mode.expiration_datetime = None
self.assertFalse(verified_mode.expiration_datetime_is_explicit)
self.assertIsNone(verified_mode.expiration_datetime)
-
- @ddt.data(
- (CourseMode.AUDIT, False),
- (CourseMode.HONOR, True),
- (CourseMode.VERIFIED, True),
- (CourseMode.CREDIT_MODE, True),
- (CourseMode.PROFESSIONAL, True),
- (CourseMode.NO_ID_PROFESSIONAL_MODE, True),
- )
- @ddt.unpack
- def test_eligible_for_cert(self, mode_slug, expected_eligibility):
- """Verify that non-audit modes are eligible for a cert."""
- self.assertEqual(CourseMode.is_eligible_for_certificate(mode_slug), expected_eligibility)
diff --git a/common/djangoapps/student/management/commands/change_enrollment.py b/common/djangoapps/student/management/commands/change_enrollment.py
index d6199a468c..8b5c1346f1 100644
--- a/common/djangoapps/student/management/commands/change_enrollment.py
+++ b/common/djangoapps/student/management/commands/change_enrollment.py
@@ -1,10 +1,24 @@
-from django.core.management.base import BaseCommand, CommandError
-from opaque_keys import InvalidKeyError
-from optparse import make_option
-from student.models import CourseEnrollment, User
+""" Command line script to change user enrollments. """
+import logging
+
+from django.core.management.base import BaseCommand, CommandError
+from django.db import transaction
+from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locations import SlashSeparatedCourseKey
+from optparse import make_option
+
+from student.models import CourseEnrollment, User
+
+logger = logging.getLogger(__name__) # pylint: disable=invalid-name
+
+
+class RollbackException(Exception):
+ """
+ Exception raised explicitly to cause a database transaction rollback.
+ """
+ pass
class Command(BaseCommand):
@@ -15,17 +29,17 @@ class Command(BaseCommand):
Example:
- Change enrollment for user joe from audit to honor:
+ Change enrollment for users joe, frank, and bill from audit to honor:
$ ... change_enrollment -u joe,frank,bill -c some/course/id --from audit --to honor
Or
- $ ... change_enrollment -u "joe@example.com,frank@example.com,bill@example.com" -c some/course/id --from audit --to honor
+ $ ... change_enrollment -e "joe@example.com,frank@example.com,bill@example.com" -c some/course/id --from audit --to honor
- Change enrollment for all users in some/course/id from audit to honor
+ See what would have been changed from audit to honor without making that change
- $ ... change_enrollment -c some/course/id --from audit --to honor
+ $ ... change_enrollment -u joe,frank,bill -c some/course/id --from audit --to honor -n
"""
@@ -40,11 +54,16 @@ class Command(BaseCommand):
dest='to_mode',
default=False,
help='move to this enrollment mode'),
- make_option('-u', '--user',
- metavar='USER',
- dest='user',
+ make_option('-u', '--usernames',
+ metavar='USERNAME',
+ dest='username',
default=False,
- help="Comma-separated list of users to move in the course"),
+ help="Comma-separated list of usernames to move in the course"),
+ make_option('-e', '--emails',
+ metavar='EMAIL',
+ dest='email',
+ default=False,
+ help="Comma-separated list of email addresses to move in the course"),
make_option('-c', '--course',
metavar='COURSE_ID',
dest='course_id',
@@ -59,8 +78,11 @@ class Command(BaseCommand):
)
def handle(self, *args, **options):
+ error_users = []
+ success_users = []
+
if not options['course_id']:
- raise CommandError("You must specify a course id for this command")
+ raise CommandError('You must specify a course id for this command')
if not options['from_mode'] or not options['to_mode']:
raise CommandError('You must specify a "to" and "from" mode as parameters')
@@ -69,26 +91,55 @@ class Command(BaseCommand):
except InvalidKeyError:
course_key = SlashSeparatedCourseKey.from_deprecated_string(options['course_id'])
- filter_args = dict(
+ enrollment_args = dict(
course_id=course_key,
mode=options['from_mode']
)
- if options['user']:
- user_str = options['user']
- for username in user_str.split(","):
- if '@' in username:
- user = User.objects.get(email=username)
- else:
- user = User.objects.get(username=username)
- filter_args['user'] = user
- enrollments = CourseEnrollment.objects.filter(**filter_args)
- if options['noop']:
- print "Would have changed {num_enrollments} students from {from_mode} to {to_mode}".format(
- num_enrollments=enrollments.count(),
- from_mode=options['from_mode'],
- to_mode=options['to_mode']
- )
- else:
+
+ if options['username']:
+ self.update_enrollments('username', enrollment_args, options, error_users, success_users)
+
+ if options['email']:
+ self.update_enrollments('email', enrollment_args, options, error_users, success_users)
+
+ self.report(error_users, success_users)
+
+ def update_enrollments(self, identifier, enrollment_args, options, error_users, success_users):
+ """ Update enrollments for a specific user identifier (email or username). """
+ users = options[identifier].split(",")
+ for identified_user in users:
+ logger.info(identified_user)
+ try:
+ user_args = {
+ identifier: identified_user
+ }
+
+ enrollment_args['user'] = User.objects.get(**user_args)
+ enrollments = CourseEnrollment.objects.filter(**enrollment_args)
+
+ with transaction.atomic():
for enrollment in enrollments:
enrollment.update_enrollment(mode=options['to_mode'])
enrollment.save()
+
+ if options['noop']:
+ raise RollbackException('Forced rollback.')
+
+ except RollbackException:
+ success_users.append(identified_user)
+ continue
+ except Exception as exception: # pylint: disable=broad-except
+ error_users.append((identified_user, exception))
+ continue
+
+ success_users.append(identified_user)
+ logger.info('Updated user [%s] to mode [%s]', identified_user, options['to_mode'])
+
+ def report(self, error_users, success_users):
+ """ Log and overview of the results of the command. """
+ total_users = len(success_users) + len(error_users)
+ logger.info('Successfully updated %i out of %i users', len(success_users), total_users)
+ if len(error_users) > 0:
+ logger.info('The following %i user(s) not saved:', len(error_users))
+ for user, error in error_users:
+ logger.info('user: [%s] reason: [%s] %s', user, type(error).__name__, error.message)
diff --git a/common/djangoapps/student/management/commands/get_grades.py b/common/djangoapps/student/management/commands/get_grades.py
index 25aff6a322..2d9dc859b5 100644
--- a/common/djangoapps/student/management/commands/get_grades.py
+++ b/common/djangoapps/student/management/commands/get_grades.py
@@ -97,9 +97,7 @@ class Command(BaseCommand):
cert_grades = {
cert.user.username: cert.grade
for cert in list(
- GeneratedCertificate.objects.filter( # pylint: disable=no-member
- course_id=course_key
- ).prefetch_related('user')
+ GeneratedCertificate.objects.filter(course_id=course_key).prefetch_related('user')
)
}
print "Grading students"
diff --git a/common/djangoapps/student/management/tests/test_change_enrollment.py b/common/djangoapps/student/management/tests/test_change_enrollment.py
new file mode 100644
index 0000000000..1d554ba47b
--- /dev/null
+++ b/common/djangoapps/student/management/tests/test_change_enrollment.py
@@ -0,0 +1,123 @@
+""" Test the change_enrollment command line script."""
+
+import ddt
+from mock import patch
+
+from django.core.management import call_command
+from xmodule.modulestore.tests.factories import CourseFactory
+from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
+
+from student.tests.factories import UserFactory, CourseModeFactory
+from student.models import CourseEnrollment
+
+
+@ddt.ddt
+class ChangeEnrollmentTests(SharedModuleStoreTestCase):
+ """ Test the enrollment change functionality of the change_enrollment script."""
+ def setUp(self):
+ super(ChangeEnrollmentTests, self).setUp()
+ self.course = CourseFactory.create()
+ self.audit_mode = CourseModeFactory.create(
+ course_id=self.course.id,
+ mode_slug='audit',
+ mode_display_name='Audit',
+ )
+ self.honor_mode = CourseModeFactory.create(
+ course_id=self.course.id,
+ mode_slug='honor',
+ mode_display_name='Honor',
+ )
+
+ self.user_info = [
+ ('amy', 'amy@pond.com', 'password'),
+ ('rory', 'rory@theroman.com', 'password'),
+ ('river', 'river@song.com', 'password')
+ ]
+ self.enrollments = []
+ self.users = []
+ for username, email, password in self.user_info:
+ user = UserFactory.create(username=username, email=email, password=password)
+ self.users.append(user)
+ self.enrollments.append(CourseEnrollment.enroll(user, self.course.id, mode='audit'))
+
+ @patch('student.management.commands.change_enrollment.logger')
+ @ddt.data(
+ ('email', False, 3),
+ ('username', False, 3),
+ ('email', True, 0),
+ ('username', True, 0),
+ )
+ @ddt.unpack
+ def test_convert_users(self, method, noop, expected_conversions, mock_logger):
+ """ The command should update the user's enrollment. """
+ user_str = ','.join([getattr(user, method) for user in self.users])
+ user_ids = [u.id for u in self.users]
+ command_args = {
+ 'course_id': unicode(self.course.id),
+ 'to_mode': 'honor',
+ 'from_mode': 'audit',
+ 'noop': noop,
+ method: user_str,
+ }
+
+ # Verify users are not in honor mode yet
+ self.assertEqual(
+ len(CourseEnrollment.objects.filter(mode='honor', user_id__in=user_ids)),
+ 0
+ )
+
+ call_command(
+ 'change_enrollment',
+ **command_args
+ )
+
+ # Verify correct number of users are now in honor mode
+ self.assertEqual(
+ len(CourseEnrollment.objects.filter(mode='honor', user_id__in=user_ids)),
+ expected_conversions
+ )
+
+ mock_logger.info.assert_called_with(
+ 'Successfully updated %i out of %i users',
+ len(self.users),
+ len(self.users)
+ )
+
+ @patch('student.management.commands.change_enrollment.logger')
+ @ddt.data(
+ ('email', 'dtennant@thedoctor.com', 3),
+ ('username', 'dtennant', 3),
+ )
+ @ddt.unpack
+ def test_user_not_found(self, method, fake_user, expected_success, mock_logger):
+ all_users = [getattr(user, method) for user in self.users]
+ all_users.append(fake_user)
+ user_str = ','.join(all_users)
+ real_user_ids = [u.id for u in self.users]
+ command_args = {
+ 'course_id': unicode(self.course.id),
+ 'to_mode': 'honor',
+ 'from_mode': 'audit',
+ method: user_str,
+ }
+
+ # Verify users are not in honor mode yet
+ self.assertEqual(
+ len(CourseEnrollment.objects.filter(mode='honor', user_id__in=real_user_ids)),
+ 0
+ )
+
+ call_command(
+ 'change_enrollment',
+ **command_args
+ )
+
+ # Verify correct number of users are now in honor mode
+ self.assertEqual(
+ len(CourseEnrollment.objects.filter(mode='honor', user_id__in=real_user_ids)),
+ expected_success
+ )
+
+ mock_logger.info.assert_called_with(
+ 'user: [%s] reason: [%s] %s', fake_user, 'DoesNotExist', 'User matching query does not exist.'
+ )
diff --git a/common/test/db_cache/bok_choy_data.json b/common/test/db_cache/bok_choy_data.json
index 7af67214dd..78dc234c40 100644
--- a/common/test/db_cache/bok_choy_data.json
+++ b/common/test/db_cache/bok_choy_data.json
@@ -1 +1 @@
-[{"fields": {"model": "permission", "app_label": "auth"}, "model": "contenttypes.contenttype", "pk": 1}, {"fields": {"model": "group", "app_label": "auth"}, "model": "contenttypes.contenttype", "pk": 2}, {"fields": {"model": "user", "app_label": "auth"}, "model": "contenttypes.contenttype", "pk": 3}, {"fields": {"model": "contenttype", "app_label": "contenttypes"}, "model": "contenttypes.contenttype", "pk": 4}, {"fields": {"model": "session", "app_label": "sessions"}, "model": "contenttypes.contenttype", "pk": 5}, {"fields": {"model": "site", "app_label": "sites"}, "model": "contenttypes.contenttype", "pk": 6}, {"fields": {"model": "taskmeta", "app_label": "djcelery"}, "model": "contenttypes.contenttype", "pk": 7}, {"fields": {"model": "tasksetmeta", "app_label": "djcelery"}, "model": "contenttypes.contenttype", "pk": 8}, {"fields": {"model": "intervalschedule", "app_label": "djcelery"}, "model": "contenttypes.contenttype", "pk": 9}, {"fields": {"model": "crontabschedule", "app_label": "djcelery"}, "model": "contenttypes.contenttype", "pk": 10}, {"fields": {"model": "periodictasks", "app_label": "djcelery"}, "model": "contenttypes.contenttype", "pk": 11}, {"fields": {"model": "periodictask", "app_label": "djcelery"}, "model": "contenttypes.contenttype", "pk": 12}, {"fields": {"model": "workerstate", "app_label": "djcelery"}, "model": "contenttypes.contenttype", "pk": 13}, {"fields": {"model": "taskstate", "app_label": "djcelery"}, "model": "contenttypes.contenttype", "pk": 14}, {"fields": {"model": "globalstatusmessage", "app_label": "status"}, "model": "contenttypes.contenttype", "pk": 15}, {"fields": {"model": "coursemessage", "app_label": "status"}, "model": "contenttypes.contenttype", "pk": 16}, {"fields": {"model": "assetbaseurlconfig", "app_label": "static_replace"}, "model": "contenttypes.contenttype", "pk": 17}, {"fields": {"model": "studentmodule", "app_label": "courseware"}, "model": "contenttypes.contenttype", "pk": 18}, {"fields": {"model": "studentmodulehistory", "app_label": "courseware"}, "model": "contenttypes.contenttype", "pk": 19}, {"fields": {"model": "xmoduleuserstatesummaryfield", "app_label": "courseware"}, "model": "contenttypes.contenttype", "pk": 20}, {"fields": {"model": "xmodulestudentprefsfield", "app_label": "courseware"}, "model": "contenttypes.contenttype", "pk": 21}, {"fields": {"model": "xmodulestudentinfofield", "app_label": "courseware"}, "model": "contenttypes.contenttype", "pk": 22}, {"fields": {"model": "offlinecomputedgrade", "app_label": "courseware"}, "model": "contenttypes.contenttype", "pk": 23}, {"fields": {"model": "offlinecomputedgradelog", "app_label": "courseware"}, "model": "contenttypes.contenttype", "pk": 24}, {"fields": {"model": "studentfieldoverride", "app_label": "courseware"}, "model": "contenttypes.contenttype", "pk": 25}, {"fields": {"model": "anonymoususerid", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 26}, {"fields": {"model": "userstanding", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 27}, {"fields": {"model": "userprofile", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 28}, {"fields": {"model": "usersignupsource", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 29}, {"fields": {"model": "usertestgroup", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 30}, {"fields": {"model": "registration", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 31}, {"fields": {"model": "pendingnamechange", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 32}, {"fields": {"model": "pendingemailchange", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 33}, {"fields": {"model": "passwordhistory", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 34}, {"fields": {"model": "loginfailures", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 35}, {"fields": {"model": "historicalcourseenrollment", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 36}, {"fields": {"model": "courseenrollment", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 37}, {"fields": {"model": "manualenrollmentaudit", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 38}, {"fields": {"model": "courseenrollmentallowed", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 39}, {"fields": {"model": "courseaccessrole", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 40}, {"fields": {"model": "dashboardconfiguration", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 41}, {"fields": {"model": "linkedinaddtoprofileconfiguration", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 42}, {"fields": {"model": "entranceexamconfiguration", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 43}, {"fields": {"model": "languageproficiency", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 44}, {"fields": {"model": "courseenrollmentattribute", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 45}, {"fields": {"model": "enrollmentrefundconfiguration", "app_label": "student"}, "model": "contenttypes.contenttype", "pk": 46}, {"fields": {"model": "trackinglog", "app_label": "track"}, "model": "contenttypes.contenttype", "pk": 47}, {"fields": {"model": "ratelimitconfiguration", "app_label": "util"}, "model": "contenttypes.contenttype", "pk": 48}, {"fields": {"model": "certificatewhitelist", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 49}, {"fields": {"model": "generatedcertificate", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 50}, {"fields": {"model": "certificategenerationhistory", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 51}, {"fields": {"model": "certificateinvalidation", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 52}, {"fields": {"model": "examplecertificateset", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 53}, {"fields": {"model": "examplecertificate", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 54}, {"fields": {"model": "certificategenerationcoursesetting", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 55}, {"fields": {"model": "certificategenerationconfiguration", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 56}, {"fields": {"model": "certificatehtmlviewconfiguration", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 57}, {"fields": {"model": "badgeassertion", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 58}, {"fields": {"model": "badgeimageconfiguration", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 59}, {"fields": {"model": "certificatetemplate", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 60}, {"fields": {"model": "certificatetemplateasset", "app_label": "certificates"}, "model": "contenttypes.contenttype", "pk": 61}, {"fields": {"model": "instructortask", "app_label": "instructor_task"}, "model": "contenttypes.contenttype", "pk": 62}, {"fields": {"model": "courseusergroup", "app_label": "course_groups"}, "model": "contenttypes.contenttype", "pk": 63}, {"fields": {"model": "cohortmembership", "app_label": "course_groups"}, "model": "contenttypes.contenttype", "pk": 64}, {"fields": {"model": "courseusergrouppartitiongroup", "app_label": "course_groups"}, "model": "contenttypes.contenttype", "pk": 65}, {"fields": {"model": "coursecohortssettings", "app_label": "course_groups"}, "model": "contenttypes.contenttype", "pk": 66}, {"fields": {"model": "coursecohort", "app_label": "course_groups"}, "model": "contenttypes.contenttype", "pk": 67}, {"fields": {"model": "courseemail", "app_label": "bulk_email"}, "model": "contenttypes.contenttype", "pk": 68}, {"fields": {"model": "optout", "app_label": "bulk_email"}, "model": "contenttypes.contenttype", "pk": 69}, {"fields": {"model": "courseemailtemplate", "app_label": "bulk_email"}, "model": "contenttypes.contenttype", "pk": 70}, {"fields": {"model": "courseauthorization", "app_label": "bulk_email"}, "model": "contenttypes.contenttype", "pk": 71}, {"fields": {"model": "brandinginfoconfig", "app_label": "branding"}, "model": "contenttypes.contenttype", "pk": 72}, {"fields": {"model": "brandingapiconfig", "app_label": "branding"}, "model": "contenttypes.contenttype", "pk": 73}, {"fields": {"model": "externalauthmap", "app_label": "external_auth"}, "model": "contenttypes.contenttype", "pk": 74}, {"fields": {"model": "nonce", "app_label": "django_openid_auth"}, "model": "contenttypes.contenttype", "pk": 75}, {"fields": {"model": "association", "app_label": "django_openid_auth"}, "model": "contenttypes.contenttype", "pk": 76}, {"fields": {"model": "useropenid", "app_label": "django_openid_auth"}, "model": "contenttypes.contenttype", "pk": 77}, {"fields": {"model": "client", "app_label": "oauth2"}, "model": "contenttypes.contenttype", "pk": 78}, {"fields": {"model": "grant", "app_label": "oauth2"}, "model": "contenttypes.contenttype", "pk": 79}, {"fields": {"model": "accesstoken", "app_label": "oauth2"}, "model": "contenttypes.contenttype", "pk": 80}, {"fields": {"model": "refreshtoken", "app_label": "oauth2"}, "model": "contenttypes.contenttype", "pk": 81}, {"fields": {"model": "trustedclient", "app_label": "oauth2_provider"}, "model": "contenttypes.contenttype", "pk": 82}, {"fields": {"model": "oauth2providerconfig", "app_label": "third_party_auth"}, "model": "contenttypes.contenttype", "pk": 83}, {"fields": {"model": "samlproviderconfig", "app_label": "third_party_auth"}, "model": "contenttypes.contenttype", "pk": 84}, {"fields": {"model": "samlconfiguration", "app_label": "third_party_auth"}, "model": "contenttypes.contenttype", "pk": 85}, {"fields": {"model": "samlproviderdata", "app_label": "third_party_auth"}, "model": "contenttypes.contenttype", "pk": 86}, {"fields": {"model": "ltiproviderconfig", "app_label": "third_party_auth"}, "model": "contenttypes.contenttype", "pk": 87}, {"fields": {"model": "providerapipermissions", "app_label": "third_party_auth"}, "model": "contenttypes.contenttype", "pk": 88}, {"fields": {"model": "nonce", "app_label": "oauth_provider"}, "model": "contenttypes.contenttype", "pk": 89}, {"fields": {"model": "scope", "app_label": "oauth_provider"}, "model": "contenttypes.contenttype", "pk": 90}, {"fields": {"model": "consumer", "app_label": "oauth_provider"}, "model": "contenttypes.contenttype", "pk": 91}, {"fields": {"model": "token", "app_label": "oauth_provider"}, "model": "contenttypes.contenttype", "pk": 92}, {"fields": {"model": "resource", "app_label": "oauth_provider"}, "model": "contenttypes.contenttype", "pk": 93}, {"fields": {"model": "article", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 94}, {"fields": {"model": "articleforobject", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 95}, {"fields": {"model": "articlerevision", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 96}, {"fields": {"model": "urlpath", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 97}, {"fields": {"model": "articleplugin", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 98}, {"fields": {"model": "reusableplugin", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 99}, {"fields": {"model": "simpleplugin", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 100}, {"fields": {"model": "revisionplugin", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 101}, {"fields": {"model": "revisionpluginrevision", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 102}, {"fields": {"model": "image", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 103}, {"fields": {"model": "imagerevision", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 104}, {"fields": {"model": "attachment", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 105}, {"fields": {"model": "attachmentrevision", "app_label": "wiki"}, "model": "contenttypes.contenttype", "pk": 106}, {"fields": {"model": "notificationtype", "app_label": "django_notify"}, "model": "contenttypes.contenttype", "pk": 107}, {"fields": {"model": "settings", "app_label": "django_notify"}, "model": "contenttypes.contenttype", "pk": 108}, {"fields": {"model": "subscription", "app_label": "django_notify"}, "model": "contenttypes.contenttype", "pk": 109}, {"fields": {"model": "notification", "app_label": "django_notify"}, "model": "contenttypes.contenttype", "pk": 110}, {"fields": {"model": "logentry", "app_label": "admin"}, "model": "contenttypes.contenttype", "pk": 111}, {"fields": {"model": "role", "app_label": "django_comment_common"}, "model": "contenttypes.contenttype", "pk": 112}, {"fields": {"model": "permission", "app_label": "django_comment_common"}, "model": "contenttypes.contenttype", "pk": 113}, {"fields": {"model": "note", "app_label": "notes"}, "model": "contenttypes.contenttype", "pk": 114}, {"fields": {"model": "splashconfig", "app_label": "splash"}, "model": "contenttypes.contenttype", "pk": 115}, {"fields": {"model": "userpreference", "app_label": "user_api"}, "model": "contenttypes.contenttype", "pk": 116}, {"fields": {"model": "usercoursetag", "app_label": "user_api"}, "model": "contenttypes.contenttype", "pk": 117}, {"fields": {"model": "userorgtag", "app_label": "user_api"}, "model": "contenttypes.contenttype", "pk": 118}, {"fields": {"model": "order", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 119}, {"fields": {"model": "orderitem", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 120}, {"fields": {"model": "invoice", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 121}, {"fields": {"model": "invoicetransaction", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 122}, {"fields": {"model": "invoiceitem", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 123}, {"fields": {"model": "courseregistrationcodeinvoiceitem", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 124}, {"fields": {"model": "invoicehistory", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 125}, {"fields": {"model": "courseregistrationcode", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 126}, {"fields": {"model": "registrationcoderedemption", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 127}, {"fields": {"model": "coupon", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 128}, {"fields": {"model": "couponredemption", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 129}, {"fields": {"model": "paidcourseregistration", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 130}, {"fields": {"model": "courseregcodeitem", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 131}, {"fields": {"model": "courseregcodeitemannotation", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 132}, {"fields": {"model": "paidcourseregistrationannotation", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 133}, {"fields": {"model": "certificateitem", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 134}, {"fields": {"model": "donationconfiguration", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 135}, {"fields": {"model": "donation", "app_label": "shoppingcart"}, "model": "contenttypes.contenttype", "pk": 136}, {"fields": {"model": "coursemode", "app_label": "course_modes"}, "model": "contenttypes.contenttype", "pk": 137}, {"fields": {"model": "coursemodesarchive", "app_label": "course_modes"}, "model": "contenttypes.contenttype", "pk": 138}, {"fields": {"model": "coursemodeexpirationconfig", "app_label": "course_modes"}, "model": "contenttypes.contenttype", "pk": 139}, {"fields": {"model": "softwaresecurephotoverification", "app_label": "verify_student"}, "model": "contenttypes.contenttype", "pk": 140}, {"fields": {"model": "historicalverificationdeadline", "app_label": "verify_student"}, "model": "contenttypes.contenttype", "pk": 141}, {"fields": {"model": "verificationdeadline", "app_label": "verify_student"}, "model": "contenttypes.contenttype", "pk": 142}, {"fields": {"model": "verificationcheckpoint", "app_label": "verify_student"}, "model": "contenttypes.contenttype", "pk": 143}, {"fields": {"model": "verificationstatus", "app_label": "verify_student"}, "model": "contenttypes.contenttype", "pk": 144}, {"fields": {"model": "incoursereverificationconfiguration", "app_label": "verify_student"}, "model": "contenttypes.contenttype", "pk": 145}, {"fields": {"model": "icrvstatusemailsconfiguration", "app_label": "verify_student"}, "model": "contenttypes.contenttype", "pk": 146}, {"fields": {"model": "skippedreverification", "app_label": "verify_student"}, "model": "contenttypes.contenttype", "pk": 147}, {"fields": {"model": "darklangconfig", "app_label": "dark_lang"}, "model": "contenttypes.contenttype", "pk": 148}, {"fields": {"model": "embargoedcourse", "app_label": "embargo"}, "model": "contenttypes.contenttype", "pk": 149}, {"fields": {"model": "embargoedstate", "app_label": "embargo"}, "model": "contenttypes.contenttype", "pk": 150}, {"fields": {"model": "restrictedcourse", "app_label": "embargo"}, "model": "contenttypes.contenttype", "pk": 151}, {"fields": {"model": "country", "app_label": "embargo"}, "model": "contenttypes.contenttype", "pk": 152}, {"fields": {"model": "countryaccessrule", "app_label": "embargo"}, "model": "contenttypes.contenttype", "pk": 153}, {"fields": {"model": "courseaccessrulehistory", "app_label": "embargo"}, "model": "contenttypes.contenttype", "pk": 154}, {"fields": {"model": "ipfilter", "app_label": "embargo"}, "model": "contenttypes.contenttype", "pk": 155}, {"fields": {"model": "coursererunstate", "app_label": "course_action_state"}, "model": "contenttypes.contenttype", "pk": 156}, {"fields": {"model": "mobileapiconfig", "app_label": "mobile_api"}, "model": "contenttypes.contenttype", "pk": 157}, {"fields": {"model": "usersocialauth", "app_label": "default"}, "model": "contenttypes.contenttype", "pk": 158}, {"fields": {"model": "nonce", "app_label": "default"}, "model": "contenttypes.contenttype", "pk": 159}, {"fields": {"model": "association", "app_label": "default"}, "model": "contenttypes.contenttype", "pk": 160}, {"fields": {"model": "code", "app_label": "default"}, "model": "contenttypes.contenttype", "pk": 161}, {"fields": {"model": "surveyform", "app_label": "survey"}, "model": "contenttypes.contenttype", "pk": 162}, {"fields": {"model": "surveyanswer", "app_label": "survey"}, "model": "contenttypes.contenttype", "pk": 163}, {"fields": {"model": "xblockasidesconfig", "app_label": "lms_xblock"}, "model": "contenttypes.contenttype", "pk": 164}, {"fields": {"model": "courseoverview", "app_label": "course_overviews"}, "model": "contenttypes.contenttype", "pk": 165}, {"fields": {"model": "courseoverviewtab", "app_label": "course_overviews"}, "model": "contenttypes.contenttype", "pk": 166}, {"fields": {"model": "courseoverviewimageset", "app_label": "course_overviews"}, "model": "contenttypes.contenttype", "pk": 167}, {"fields": {"model": "courseoverviewimageconfig", "app_label": "course_overviews"}, "model": "contenttypes.contenttype", "pk": 168}, {"fields": {"model": "coursestructure", "app_label": "course_structures"}, "model": "contenttypes.contenttype", "pk": 169}, {"fields": {"model": "corsmodel", "app_label": "corsheaders"}, "model": "contenttypes.contenttype", "pk": 170}, {"fields": {"model": "xdomainproxyconfiguration", "app_label": "cors_csrf"}, "model": "contenttypes.contenttype", "pk": 171}, {"fields": {"model": "creditprovider", "app_label": "credit"}, "model": "contenttypes.contenttype", "pk": 172}, {"fields": {"model": "creditcourse", "app_label": "credit"}, "model": "contenttypes.contenttype", "pk": 173}, {"fields": {"model": "creditrequirement", "app_label": "credit"}, "model": "contenttypes.contenttype", "pk": 174}, {"fields": {"model": "historicalcreditrequirementstatus", "app_label": "credit"}, "model": "contenttypes.contenttype", "pk": 175}, {"fields": {"model": "creditrequirementstatus", "app_label": "credit"}, "model": "contenttypes.contenttype", "pk": 176}, {"fields": {"model": "crediteligibility", "app_label": "credit"}, "model": "contenttypes.contenttype", "pk": 177}, {"fields": {"model": "historicalcreditrequest", "app_label": "credit"}, "model": "contenttypes.contenttype", "pk": 178}, {"fields": {"model": "creditrequest", "app_label": "credit"}, "model": "contenttypes.contenttype", "pk": 179}, {"fields": {"model": "courseteam", "app_label": "teams"}, "model": "contenttypes.contenttype", "pk": 180}, {"fields": {"model": "courseteammembership", "app_label": "teams"}, "model": "contenttypes.contenttype", "pk": 181}, {"fields": {"model": "xblockdisableconfig", "app_label": "xblock_django"}, "model": "contenttypes.contenttype", "pk": 182}, {"fields": {"model": "bookmark", "app_label": "bookmarks"}, "model": "contenttypes.contenttype", "pk": 183}, {"fields": {"model": "xblockcache", "app_label": "bookmarks"}, "model": "contenttypes.contenttype", "pk": 184}, {"fields": {"model": "programsapiconfig", "app_label": "programs"}, "model": "contenttypes.contenttype", "pk": 185}, {"fields": {"model": "selfpacedconfiguration", "app_label": "self_paced"}, "model": "contenttypes.contenttype", "pk": 186}, {"fields": {"model": "kvstore", "app_label": "thumbnail"}, "model": "contenttypes.contenttype", "pk": 187}, {"fields": {"model": "studentitem", "app_label": "submissions"}, "model": "contenttypes.contenttype", "pk": 188}, {"fields": {"model": "submission", "app_label": "submissions"}, "model": "contenttypes.contenttype", "pk": 189}, {"fields": {"model": "score", "app_label": "submissions"}, "model": "contenttypes.contenttype", "pk": 190}, {"fields": {"model": "scoresummary", "app_label": "submissions"}, "model": "contenttypes.contenttype", "pk": 191}, {"fields": {"model": "scoreannotation", "app_label": "submissions"}, "model": "contenttypes.contenttype", "pk": 192}, {"fields": {"model": "rubric", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 193}, {"fields": {"model": "criterion", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 194}, {"fields": {"model": "criterionoption", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 195}, {"fields": {"model": "assessment", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 196}, {"fields": {"model": "assessmentpart", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 197}, {"fields": {"model": "assessmentfeedbackoption", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 198}, {"fields": {"model": "assessmentfeedback", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 199}, {"fields": {"model": "peerworkflow", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 200}, {"fields": {"model": "peerworkflowitem", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 201}, {"fields": {"model": "trainingexample", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 202}, {"fields": {"model": "studenttrainingworkflow", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 203}, {"fields": {"model": "studenttrainingworkflowitem", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 204}, {"fields": {"model": "aiclassifierset", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 205}, {"fields": {"model": "aiclassifier", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 206}, {"fields": {"model": "aitrainingworkflow", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 207}, {"fields": {"model": "aigradingworkflow", "app_label": "assessment"}, "model": "contenttypes.contenttype", "pk": 208}, {"fields": {"model": "assessmentworkflow", "app_label": "workflow"}, "model": "contenttypes.contenttype", "pk": 209}, {"fields": {"model": "assessmentworkflowstep", "app_label": "workflow"}, "model": "contenttypes.contenttype", "pk": 210}, {"fields": {"model": "assessmentworkflowcancellation", "app_label": "workflow"}, "model": "contenttypes.contenttype", "pk": 211}, {"fields": {"model": "profile", "app_label": "edxval"}, "model": "contenttypes.contenttype", "pk": 212}, {"fields": {"model": "video", "app_label": "edxval"}, "model": "contenttypes.contenttype", "pk": 213}, {"fields": {"model": "coursevideo", "app_label": "edxval"}, "model": "contenttypes.contenttype", "pk": 214}, {"fields": {"model": "encodedvideo", "app_label": "edxval"}, "model": "contenttypes.contenttype", "pk": 215}, {"fields": {"model": "subtitle", "app_label": "edxval"}, "model": "contenttypes.contenttype", "pk": 216}, {"fields": {"model": "milestone", "app_label": "milestones"}, "model": "contenttypes.contenttype", "pk": 217}, {"fields": {"model": "milestonerelationshiptype", "app_label": "milestones"}, "model": "contenttypes.contenttype", "pk": 218}, {"fields": {"model": "coursemilestone", "app_label": "milestones"}, "model": "contenttypes.contenttype", "pk": 219}, {"fields": {"model": "coursecontentmilestone", "app_label": "milestones"}, "model": "contenttypes.contenttype", "pk": 220}, {"fields": {"model": "usermilestone", "app_label": "milestones"}, "model": "contenttypes.contenttype", "pk": 221}, {"fields": {"model": "proctoredexam", "app_label": "edx_proctoring"}, "model": "contenttypes.contenttype", "pk": 222}, {"fields": {"model": "proctoredexamreviewpolicy", "app_label": "edx_proctoring"}, "model": "contenttypes.contenttype", "pk": 223}, {"fields": {"model": "proctoredexamreviewpolicyhistory", "app_label": "edx_proctoring"}, "model": "contenttypes.contenttype", "pk": 224}, {"fields": {"model": "proctoredexamstudentattempt", "app_label": "edx_proctoring"}, "model": "contenttypes.contenttype", "pk": 225}, {"fields": {"model": "proctoredexamstudentattempthistory", "app_label": "edx_proctoring"}, "model": "contenttypes.contenttype", "pk": 226}, {"fields": {"model": "proctoredexamstudentallowance", "app_label": "edx_proctoring"}, "model": "contenttypes.contenttype", "pk": 227}, {"fields": {"model": "proctoredexamstudentallowancehistory", "app_label": "edx_proctoring"}, "model": "contenttypes.contenttype", "pk": 228}, {"fields": {"model": "proctoredexamsoftwaresecurereview", "app_label": "edx_proctoring"}, "model": "contenttypes.contenttype", "pk": 229}, {"fields": {"model": "proctoredexamsoftwaresecurereviewhistory", "app_label": "edx_proctoring"}, "model": "contenttypes.contenttype", "pk": 230}, {"fields": {"model": "proctoredexamsoftwaresecurecomment", "app_label": "edx_proctoring"}, "model": "contenttypes.contenttype", "pk": 231}, {"fields": {"model": "organization", "app_label": "organizations"}, "model": "contenttypes.contenttype", "pk": 232}, {"fields": {"model": "organizationcourse", "app_label": "organizations"}, "model": "contenttypes.contenttype", "pk": 233}, {"fields": {"model": "videouploadconfig", "app_label": "contentstore"}, "model": "contenttypes.contenttype", "pk": 234}, {"fields": {"model": "pushnotificationconfig", "app_label": "contentstore"}, "model": "contenttypes.contenttype", "pk": 235}, {"fields": {"model": "coursecreator", "app_label": "course_creators"}, "model": "contenttypes.contenttype", "pk": 236}, {"fields": {"model": "studioconfig", "app_label": "xblock_config"}, "model": "contenttypes.contenttype", "pk": 237}, {"fields": {"domain": "example.com", "name": "example.com"}, "model": "sites.site", "pk": 1}, {"fields": {"default": false, "mode": "honor", "icon": "badges/honor_NHmjd1w.png"}, "model": "certificates.badgeimageconfiguration", "pk": 1}, {"fields": {"default": false, "mode": "verified", "icon": "badges/verified_UkIJhGD.png"}, "model": "certificates.badgeimageconfiguration", "pk": 2}, {"fields": {"default": false, "mode": "professional", "icon": "badges/professional_B83oKqK.png"}, "model": "certificates.badgeimageconfiguration", "pk": 3}, {"fields": {"plain_template": "{course_title}\n\n{{message_body}}\r\n----\r\nCopyright 2013 edX, All rights reserved.\r\n----\r\nConnect with edX:\r\nFacebook (http://facebook.com/edxonline)\r\nTwitter (http://twitter.com/edxonline)\r\nGoogle+ (https://plus.google.com/108235383044095082735)\r\nMeetup (http://www.meetup.com/edX-Communities/)\r\n----\r\nThis email was automatically sent from {platform_name}.\r\nYou are receiving this email at address {email} because you are enrolled in {course_title}\r\n(URL: {course_url} ).\r\nTo stop receiving email like this, update your course email settings at {email_settings_url}.\r\n", "html_template": "
Update from {course_title}