diff --git a/cms/envs/common.py b/cms/envs/common.py
index a557f2c649..b60cec9db7 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -78,6 +78,9 @@ FEATURES = {
# Allow editing of short description in course settings in cms
'EDITABLE_SHORT_DESCRIPTION': True,
+
+ # Toggles embargo functionality
+ 'EMBARGO': False,
}
ENABLE_JASMINE = False
diff --git a/cms/envs/test.py b/cms/envs/test.py
index 5edbc89686..7077ed87bd 100644
--- a/cms/envs/test.py
+++ b/cms/envs/test.py
@@ -190,3 +190,6 @@ FEATURES['ENABLE_SERVICE_STATUS'] = True
# This is to disable a test under the common directory that will not pass when run under CMS
FEATURES['DISABLE_RESET_EMAIL_TEST'] = True
+
+# Toggles embargo on for testing
+FEATURES['EMBARGO'] = True
diff --git a/common/djangoapps/embargo/admin.py b/common/djangoapps/embargo/admin.py
index 75a9f11d81..8ff4cc3970 100644
--- a/common/djangoapps/embargo/admin.py
+++ b/common/djangoapps/embargo/admin.py
@@ -15,14 +15,15 @@ class EmbargoedCourseAdmin(admin.ModelAdmin):
fieldsets = (
(None, {
'fields': ('course_id', 'embargoed'),
- 'description': textwrap.dedent("""Enter a course id in the following box.
- Do not enter leading or trailing slashes. There is no need to surround the
- course ID with quotes.
- Validation will be performed on the course name, and if it is invalid, an
- error message will display.
+ 'description': textwrap.dedent("""\
+ Enter a course id in the following box.
+ Do not enter leading or trailing slashes. There is no need to surround the
+ course ID with quotes.
+ Validation will be performed on the course name, and if it is invalid, an
+ error message will display.
- To enable embargos against this course (restrict course access from embargoed
- states), check the "Embargoed" box, then click "Save".
+ To enable embargos against this course (restrict course access from embargoed
+ states), check the "Embargoed" box, then click "Save".
""")
}),
)
diff --git a/common/djangoapps/embargo/forms.py b/common/djangoapps/embargo/forms.py
index 39f03c5211..09ea11445b 100644
--- a/common/djangoapps/embargo/forms.py
+++ b/common/djangoapps/embargo/forms.py
@@ -9,9 +9,7 @@ from embargo.fixtures.country_codes import COUNTRY_CODES
import socket
-from xmodule.course_module import CourseDescriptor
from xmodule.modulestore.django import modulestore
-from xmodule.modulestore.exceptions import ItemNotFoundError, InvalidLocationError
class EmbargoedCourseForm(forms.ModelForm): # pylint: disable=incomplete-protocol
@@ -23,18 +21,17 @@ class EmbargoedCourseForm(forms.ModelForm): # pylint: disable=incomplete-protoc
def clean_course_id(self):
"""Validate the course id"""
course_id = self.cleaned_data["course_id"]
+
+ # Try to get the course. If this returns None, it's not a real course
try:
- # Try to get the course descriptor, if we can do that,
- # it's a real course.
- course_loc = CourseDescriptor.id_to_location(course_id)
- modulestore().get_instance(course_id, course_loc, depth=1)
- except (KeyError, ItemNotFoundError):
+ course = modulestore().get_course(course_id)
+ except ValueError:
msg = 'COURSE NOT FOUND'
msg += u' --- Entered course id was: "{0}". '.format(course_id)
msg += 'Please recheck that you have supplied a valid course id.'
raise forms.ValidationError(msg)
- except (ValueError, InvalidLocationError):
- msg = 'INVALID LOCATION'
+ if not course:
+ msg = 'COURSE NOT FOUND'
msg += u' --- Entered course id was: "{0}". '.format(course_id)
msg += 'Please recheck that you have supplied a valid course id.'
raise forms.ValidationError(msg)
@@ -50,14 +47,12 @@ class EmbargoedStateForm(forms.ModelForm): # pylint: disable=incomplete-protoco
def _is_valid_code(self, code):
"""Whether or not code is a valid country code"""
- if code in COUNTRY_CODES:
- return True
- return False
+ return code in COUNTRY_CODES
def clean_embargoed_countries(self):
"""Validate the country list"""
embargoed_countries = self.cleaned_data["embargoed_countries"]
- if embargoed_countries == '':
+ if not embargoed_countries:
return ''
error_countries = []
diff --git a/common/djangoapps/embargo/middleware.py b/common/djangoapps/embargo/middleware.py
index 4fdf4d803f..2e2cb2b645 100644
--- a/common/djangoapps/embargo/middleware.py
+++ b/common/djangoapps/embargo/middleware.py
@@ -9,8 +9,8 @@ HTTP_X_FORWARDED_FOR).
"""
import pygeoip
-import django.core.exceptions
+from django.core.exceptions import MiddlewareNotUsed
from django.conf import settings
from django.shortcuts import redirect
from ipware.ip import get_ip
@@ -29,7 +29,7 @@ class EmbargoMiddleware(object):
def __init__(self):
# If embargoing is turned off, make this middleware do nothing
if not settings.FEATURES.get('EMBARGO', False):
- raise django.core.exceptions.MiddlewareNotUsed()
+ raise MiddlewareNotUsed()
def process_request(self, request):
"""
diff --git a/common/djangoapps/embargo/tests/test_forms.py b/common/djangoapps/embargo/tests/test_forms.py
index 7fc7682642..cea030c23d 100644
--- a/common/djangoapps/embargo/tests/test_forms.py
+++ b/common/djangoapps/embargo/tests/test_forms.py
@@ -25,14 +25,6 @@ class EmbargoCourseFormTest(ModuleStoreTestCase):
self.true_form_data = {'course_id': self.course.id, 'embargoed': True}
self.false_form_data = {'course_id': self.course.id, 'embargoed': False}
- def tearDown(self):
- # Delete any EmbargoedCourse record we may have created
- try:
- record = EmbargoedCourse.objects.get(course_id=self.course.id)
- record.delete()
- except EmbargoedCourse.DoesNotExist:
- return
-
def test_embargo_course(self):
self.assertFalse(EmbargoedCourse.is_embargoed(self.course.id))
# Test adding embargo to this course
@@ -94,7 +86,7 @@ class EmbargoCourseFormTest(ModuleStoreTestCase):
# Validation shouldn't work
self.assertFalse(form.is_valid())
- msg = 'INVALID LOCATION'
+ msg = 'COURSE NOT FOUND'
msg += u' --- Entered course id was: "{0}". '.format(bad_id)
msg += 'Please recheck that you have supplied a valid course id.'
self.assertEquals(msg, form._errors['course_id'][0]) # pylint: disable=protected-access
@@ -106,6 +98,10 @@ class EmbargoCourseFormTest(ModuleStoreTestCase):
class EmbargoedStateFormTest(TestCase):
"""Test form for adding new states"""
+ def setUp(self):
+ # Explicitly clear the cache, since ConfigurationModel relies on the cache
+ cache.clear()
+
def tearDown(self):
# Explicitly clear ConfigurationModel's cache so tests have a clear cache
# and don't interfere with each other
@@ -128,7 +124,6 @@ class EmbargoedStateFormTest(TestCase):
form.save()
self.assertTrue(len(EmbargoedState.current().embargoed_countries_list) == 0)
-
def test_add_invalid_states(self):
# test adding invalid codes
# xx is not valid
diff --git a/common/djangoapps/embargo/tests/test_middleware.py b/common/djangoapps/embargo/tests/test_middleware.py
index 3e39c93c30..c3f418c905 100644
--- a/common/djangoapps/embargo/tests/test_middleware.py
+++ b/common/djangoapps/embargo/tests/test_middleware.py
@@ -45,10 +45,14 @@ class EmbargoMiddlewareTests(TestCase):
# Text from lms/templates/static_templates/embargo.html
self.embargo_text = "Unfortunately, at this time edX must comply with export controls, and we cannot allow you to access this particular course."
+ self.patcher = mock.patch.object(pygeoip.GeoIP, 'country_code_by_addr', self.mock_country_code_by_addr)
+ self.patcher.start()
+
def tearDown(self):
# Explicitly clear ConfigurationModel's cache so tests have a clear cache
# and don't interfere with each other
cache.clear()
+ self.patcher.stop()
def mock_country_code_by_addr(self, ip_addr):
"""
@@ -65,32 +69,29 @@ class EmbargoMiddlewareTests(TestCase):
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_countries(self):
- with mock.patch.object(pygeoip.GeoIP, 'country_code_by_addr') as mocked_method:
- mocked_method.side_effect = self.mock_country_code_by_addr
+ # Accessing an embargoed page from a blocked IP should cause a redirect
+ response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0')
+ self.assertEqual(response.status_code, 302)
+ # Following the redirect should give us the embargo page
+ response = self.client.get(
+ self.embargoed_page,
+ HTTP_X_FORWARDED_FOR='1.0.0.0',
+ REMOTE_ADDR='1.0.0.0',
+ follow=True
+ )
+ self.assertIn(self.embargo_text, response.content)
- # Accessing an embargoed page from a blocked IP should cause a redirect
- response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0')
- self.assertEqual(response.status_code, 302)
- # Following the redirect should give us the embargo page
- response = self.client.get(
- self.embargoed_page,
- HTTP_X_FORWARDED_FOR='1.0.0.0',
- REMOTE_ADDR='1.0.0.0',
- follow=True
- )
- self.assertIn(self.embargo_text, response.content)
+ # Accessing a regular page from a blocked IP should succeed
+ response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0')
+ self.assertEqual(response.status_code, 200)
- # Accessing a regular page from a blocked IP should succeed
- response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0')
- self.assertEqual(response.status_code, 200)
+ # Accessing an embargoed page from a non-embargoed IP should succeed
+ response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0')
+ self.assertEqual(response.status_code, 200)
- # Accessing an embargoed page from a non-embargoed IP should succeed
- response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0')
- self.assertEqual(response.status_code, 200)
-
- # Accessing a regular page from a non-embargoed IP should succeed
- response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0')
- self.assertEqual(response.status_code, 200)
+ # Accessing a regular page from a non-embargoed IP should succeed
+ response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0')
+ self.assertEqual(response.status_code, 200)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_ip_exceptions(self):
@@ -102,31 +103,57 @@ class EmbargoMiddlewareTests(TestCase):
enabled=True
).save()
- with mock.patch.object(pygeoip.GeoIP, 'country_code_by_addr') as mocked_method:
- mocked_method.side_effect = self.mock_country_code_by_addr
+ # Accessing an embargoed page from a blocked IP that's been whitelisted
+ # should succeed
+ response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0')
+ self.assertEqual(response.status_code, 200)
- # Accessing an embargoed page from a blocked IP that's been whitelisted
- # should succeed
- response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0')
- self.assertEqual(response.status_code, 200)
+ # Accessing a regular course from a blocked IP that's been whitelisted should succeed
+ response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0')
+ self.assertEqual(response.status_code, 200)
- # Accessing a regular course from a blocked IP that's been whitelisted should succeed
- response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0')
- self.assertEqual(response.status_code, 200)
+ # Accessing an embargoed course from non-embargoed IP that's been blacklisted
+ # should cause a redirect
+ response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0')
+ self.assertEqual(response.status_code, 302)
+ # Following the redirect should give us the embargo page
+ response = self.client.get(
+ self.embargoed_page,
+ HTTP_X_FORWARDED_FOR='5.0.0.0',
+ REMOTE_ADDR='1.0.0.0',
+ follow=True
+ )
+ self.assertIn(self.embargo_text, response.content)
- # Accessing an embargoed course from non-embargoed IP that's been blacklisted
- # should cause a redirect
- response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0')
- self.assertEqual(response.status_code, 302)
- # Following the redirect should give us the embargo page
- response = self.client.get(
- self.embargoed_page,
- HTTP_X_FORWARDED_FOR='5.0.0.0',
- REMOTE_ADDR='1.0.0.0',
- follow=True
- )
- self.assertIn(self.embargo_text, response.content)
+ # Accessing a regular course from a non-embargoed IP that's been blacklisted should succeed
+ response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0')
+ self.assertEqual(response.status_code, 200)
- # Accessing a regular course from a non-embargoed IP that's been blacklisted should succeed
- response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0')
- self.assertEqual(response.status_code, 200)
+ @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
+ @mock.patch.dict(settings.FEATURES, {'EMBARGO': False})
+ def test_countries_embargo_off(self):
+ # When the middleware is turned off, all requests should go through
+ # Accessing an embargoed page from a blocked IP OK
+ response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0')
+ self.assertEqual(response.status_code, 200)
+
+ # Accessing a regular page from a blocked IP should succeed
+ response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0')
+ self.assertEqual(response.status_code, 200)
+
+ # Explicitly whitelist/blacklist some IPs
+ IPFilter(
+ whitelist='1.0.0.0',
+ blacklist='5.0.0.0',
+ changed_by=self.user,
+ enabled=True
+ ).save()
+
+ # Accessing an embargoed course from non-embargoed IP that's been blacklisted
+ # should be OK
+ response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0')
+ self.assertEqual(response.status_code, 200)
+
+ # Accessing a regular course from a non-embargoed IP that's been blacklisted should succeed
+ response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0')
+ self.assertEqual(response.status_code, 200)
diff --git a/common/test/db_cache/bok_choy_data.json b/common/test/db_cache/bok_choy_data.json
index c485e0decd..e622c88927 100644
--- a/common/test/db_cache/bok_choy_data.json
+++ b/common/test/db_cache/bok_choy_data.json
@@ -1 +1 @@
-[{"pk": 30, "model": "contenttypes.contenttype", "fields": {"model": "anonymoususerid", "name": "anonymous user id", "app_label": "student"}}, {"pk": 50, "model": "contenttypes.contenttype", "fields": {"model": "article", "name": "article", "app_label": "wiki"}}, {"pk": 51, "model": "contenttypes.contenttype", "fields": {"model": "articleforobject", "name": "Article for object", "app_label": "wiki"}}, {"pk": 54, "model": "contenttypes.contenttype", "fields": {"model": "articleplugin", "name": "article plugin", "app_label": "wiki"}}, {"pk": 52, "model": "contenttypes.contenttype", "fields": {"model": "articlerevision", "name": "article revision", "app_label": "wiki"}}, {"pk": 59, "model": "contenttypes.contenttype", "fields": {"model": "articlesubscription", "name": "article subscription", "app_label": "wiki"}}, {"pk": 20, "model": "contenttypes.contenttype", "fields": {"model": "association", "name": "association", "app_label": "django_openid_auth"}}, {"pk": 78, "model": "contenttypes.contenttype", "fields": {"model": "certificateitem", "name": "certificate item", "app_label": "shoppingcart"}}, {"pk": 40, "model": "contenttypes.contenttype", "fields": {"model": "certificatewhitelist", "name": "certificate whitelist", "app_label": "certificates"}}, {"pk": 4, "model": "contenttypes.contenttype", "fields": {"model": "contenttype", "name": "content type", "app_label": "contenttypes"}}, {"pk": 48, "model": "contenttypes.contenttype", "fields": {"model": "courseauthorization", "name": "course authorization", "app_label": "bulk_email"}}, {"pk": 45, "model": "contenttypes.contenttype", "fields": {"model": "courseemail", "name": "course email", "app_label": "bulk_email"}}, {"pk": 47, "model": "contenttypes.contenttype", "fields": {"model": "courseemailtemplate", "name": "course email template", "app_label": "bulk_email"}}, {"pk": 37, "model": "contenttypes.contenttype", "fields": {"model": "courseenrollment", "name": "course enrollment", "app_label": "student"}}, {"pk": 38, "model": "contenttypes.contenttype", "fields": {"model": "courseenrollmentallowed", "name": "course enrollment allowed", "app_label": "student"}}, {"pk": 71, "model": "contenttypes.contenttype", "fields": {"model": "coursemode", "name": "course mode", "app_label": "course_modes"}}, {"pk": 43, "model": "contenttypes.contenttype", "fields": {"model": "coursesoftware", "name": "course software", "app_label": "licenses"}}, {"pk": 18, "model": "contenttypes.contenttype", "fields": {"model": "courseusergroup", "name": "course user group", "app_label": "course_groups"}}, {"pk": 10, "model": "contenttypes.contenttype", "fields": {"model": "crontabschedule", "name": "crontab", "app_label": "djcelery"}}, {"pk": 49, "model": "contenttypes.contenttype", "fields": {"model": "externalauthmap", "name": "external auth map", "app_label": "external_auth"}}, {"pk": 66, "model": "contenttypes.contenttype", "fields": {"model": "flag", "name": "flag", "app_label": "waffle"}}, {"pk": 41, "model": "contenttypes.contenttype", "fields": {"model": "generatedcertificate", "name": "generated certificate", "app_label": "certificates"}}, {"pk": 2, "model": "contenttypes.contenttype", "fields": {"model": "group", "name": "group", "app_label": "auth"}}, {"pk": 42, "model": "contenttypes.contenttype", "fields": {"model": "instructortask", "name": "instructor task", "app_label": "instructor_task"}}, {"pk": 9, "model": "contenttypes.contenttype", "fields": {"model": "intervalschedule", "name": "interval", "app_label": "djcelery"}}, {"pk": 73, "model": "contenttypes.contenttype", "fields": {"model": "linkedin", "name": "linked in", "app_label": "linkedin"}}, {"pk": 22, "model": "contenttypes.contenttype", "fields": {"model": "logentry", "name": "log entry", "app_label": "admin"}}, {"pk": 15, "model": "contenttypes.contenttype", "fields": {"model": "migrationhistory", "name": "migration history", "app_label": "south"}}, {"pk": 19, "model": "contenttypes.contenttype", "fields": {"model": "nonce", "name": "nonce", "app_label": "django_openid_auth"}}, {"pk": 69, "model": "contenttypes.contenttype", "fields": {"model": "note", "name": "note", "app_label": "notes"}}, {"pk": 63, "model": "contenttypes.contenttype", "fields": {"model": "notification", "name": "notification", "app_label": "django_notify"}}, {"pk": 28, "model": "contenttypes.contenttype", "fields": {"model": "offlinecomputedgrade", "name": "offline computed grade", "app_label": "courseware"}}, {"pk": 29, "model": "contenttypes.contenttype", "fields": {"model": "offlinecomputedgradelog", "name": "offline computed grade log", "app_label": "courseware"}}, {"pk": 46, "model": "contenttypes.contenttype", "fields": {"model": "optout", "name": "optout", "app_label": "bulk_email"}}, {"pk": 74, "model": "contenttypes.contenttype", "fields": {"model": "order", "name": "order", "app_label": "shoppingcart"}}, {"pk": 75, "model": "contenttypes.contenttype", "fields": {"model": "orderitem", "name": "order item", "app_label": "shoppingcart"}}, {"pk": 76, "model": "contenttypes.contenttype", "fields": {"model": "paidcourseregistration", "name": "paid course registration", "app_label": "shoppingcart"}}, {"pk": 77, "model": "contenttypes.contenttype", "fields": {"model": "paidcourseregistrationannotation", "name": "paid course registration annotation", "app_label": "shoppingcart"}}, {"pk": 36, "model": "contenttypes.contenttype", "fields": {"model": "pendingemailchange", "name": "pending email change", "app_label": "student"}}, {"pk": 35, "model": "contenttypes.contenttype", "fields": {"model": "pendingnamechange", "name": "pending name change", "app_label": "student"}}, {"pk": 12, "model": "contenttypes.contenttype", "fields": {"model": "periodictask", "name": "periodic task", "app_label": "djcelery"}}, {"pk": 11, "model": "contenttypes.contenttype", "fields": {"model": "periodictasks", "name": "periodic tasks", "app_label": "djcelery"}}, {"pk": 1, "model": "contenttypes.contenttype", "fields": {"model": "permission", "name": "permission", "app_label": "auth"}}, {"pk": 17, "model": "contenttypes.contenttype", "fields": {"model": "psychometricdata", "name": "psychometric data", "app_label": "psychometrics"}}, {"pk": 65, "model": "contenttypes.contenttype", "fields": {"model": "puzzlecomplete", "name": "puzzle complete", "app_label": "foldit"}}, {"pk": 34, "model": "contenttypes.contenttype", "fields": {"model": "registration", "name": "registration", "app_label": "student"}}, {"pk": 55, "model": "contenttypes.contenttype", "fields": {"model": "reusableplugin", "name": "reusable plugin", "app_label": "wiki"}}, {"pk": 57, "model": "contenttypes.contenttype", "fields": {"model": "revisionplugin", "name": "revision plugin", "app_label": "wiki"}}, {"pk": 58, "model": "contenttypes.contenttype", "fields": {"model": "revisionpluginrevision", "name": "revision plugin revision", "app_label": "wiki"}}, {"pk": 68, "model": "contenttypes.contenttype", "fields": {"model": "sample", "name": "sample", "app_label": "waffle"}}, {"pk": 8, "model": "contenttypes.contenttype", "fields": {"model": "tasksetmeta", "name": "saved group result", "app_label": "djcelery"}}, {"pk": 64, "model": "contenttypes.contenttype", "fields": {"model": "score", "name": "score", "app_label": "foldit"}}, {"pk": 16, "model": "contenttypes.contenttype", "fields": {"model": "servercircuit", "name": "server circuit", "app_label": "circuit"}}, {"pk": 5, "model": "contenttypes.contenttype", "fields": {"model": "session", "name": "session", "app_label": "sessions"}}, {"pk": 61, "model": "contenttypes.contenttype", "fields": {"model": "settings", "name": "settings", "app_label": "django_notify"}}, {"pk": 56, "model": "contenttypes.contenttype", "fields": {"model": "simpleplugin", "name": "simple plugin", "app_label": "wiki"}}, {"pk": 6, "model": "contenttypes.contenttype", "fields": {"model": "site", "name": "site", "app_label": "sites"}}, {"pk": 72, "model": "contenttypes.contenttype", "fields": {"model": "softwaresecurephotoverification", "name": "software secure photo verification", "app_label": "verify_student"}}, {"pk": 23, "model": "contenttypes.contenttype", "fields": {"model": "studentmodule", "name": "student module", "app_label": "courseware"}}, {"pk": 24, "model": "contenttypes.contenttype", "fields": {"model": "studentmodulehistory", "name": "student module history", "app_label": "courseware"}}, {"pk": 62, "model": "contenttypes.contenttype", "fields": {"model": "subscription", "name": "subscription", "app_label": "django_notify"}}, {"pk": 67, "model": "contenttypes.contenttype", "fields": {"model": "switch", "name": "switch", "app_label": "waffle"}}, {"pk": 14, "model": "contenttypes.contenttype", "fields": {"model": "taskstate", "name": "task", "app_label": "djcelery"}}, {"pk": 7, "model": "contenttypes.contenttype", "fields": {"model": "taskmeta", "name": "task state", "app_label": "djcelery"}}, {"pk": 39, "model": "contenttypes.contenttype", "fields": {"model": "trackinglog", "name": "tracking log", "app_label": "track"}}, {"pk": 60, "model": "contenttypes.contenttype", "fields": {"model": "notificationtype", "name": "type", "app_label": "django_notify"}}, {"pk": 53, "model": "contenttypes.contenttype", "fields": {"model": "urlpath", "name": "URL path", "app_label": "wiki"}}, {"pk": 3, "model": "contenttypes.contenttype", "fields": {"model": "user", "name": "user", "app_label": "auth"}}, {"pk": 44, "model": "contenttypes.contenttype", "fields": {"model": "userlicense", "name": "user license", "app_label": "licenses"}}, {"pk": 21, "model": "contenttypes.contenttype", "fields": {"model": "useropenid", "name": "user open id", "app_label": "django_openid_auth"}}, {"pk": 70, "model": "contenttypes.contenttype", "fields": {"model": "userpreference", "name": "user preference", "app_label": "user_api"}}, {"pk": 32, "model": "contenttypes.contenttype", "fields": {"model": "userprofile", "name": "user profile", "app_label": "student"}}, {"pk": 31, "model": "contenttypes.contenttype", "fields": {"model": "userstanding", "name": "user standing", "app_label": "student"}}, {"pk": 33, "model": "contenttypes.contenttype", "fields": {"model": "usertestgroup", "name": "user test group", "app_label": "student"}}, {"pk": 13, "model": "contenttypes.contenttype", "fields": {"model": "workerstate", "name": "worker", "app_label": "djcelery"}}, {"pk": 27, "model": "contenttypes.contenttype", "fields": {"model": "xmodulestudentinfofield", "name": "x module student info field", "app_label": "courseware"}}, {"pk": 26, "model": "contenttypes.contenttype", "fields": {"model": "xmodulestudentprefsfield", "name": "x module student prefs field", "app_label": "courseware"}}, {"pk": 25, "model": "contenttypes.contenttype", "fields": {"model": "xmoduleuserstatesummaryfield", "name": "x module user state summary field", "app_label": "courseware"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:32Z", "app_name": "courseware", "migration": "0001_initial"}}, {"pk": 2, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:32Z", "app_name": "courseware", "migration": "0002_add_indexes"}}, {"pk": 3, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:32Z", "app_name": "courseware", "migration": "0003_done_grade_cache"}}, {"pk": 4, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:32Z", "app_name": "courseware", "migration": "0004_add_field_studentmodule_course_id"}}, {"pk": 5, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:33Z", "app_name": "courseware", "migration": "0005_auto__add_offlinecomputedgrade__add_unique_offlinecomputedgrade_user_c"}}, {"pk": 6, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:33Z", "app_name": "courseware", "migration": "0006_create_student_module_history"}}, {"pk": 7, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:33Z", "app_name": "courseware", "migration": "0007_allow_null_version_in_history"}}, {"pk": 8, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:33Z", "app_name": "courseware", "migration": "0008_add_xmodule_storage"}}, {"pk": 9, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:33Z", "app_name": "courseware", "migration": "0009_add_field_default"}}, {"pk": 10, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:33Z", "app_name": "courseware", "migration": "0010_rename_xblock_field_content_to_user_state_summary"}}, {"pk": 11, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:33Z", "app_name": "student", "migration": "0001_initial"}}, {"pk": 12, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:33Z", "app_name": "student", "migration": "0002_text_to_varchar_and_indexes"}}, {"pk": 13, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:33Z", "app_name": "student", "migration": "0003_auto__add_usertestgroup"}}, {"pk": 14, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0004_add_email_index"}}, {"pk": 15, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0005_name_change"}}, {"pk": 16, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0006_expand_meta_field"}}, {"pk": 17, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0007_convert_to_utf8"}}, {"pk": 18, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0008__auto__add_courseregistration"}}, {"pk": 19, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0009_auto__del_courseregistration__add_courseenrollment"}}, {"pk": 20, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0010_auto__chg_field_courseenrollment_course_id"}}, {"pk": 21, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0011_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use"}}, {"pk": 22, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0012_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt"}}, {"pk": 23, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0013_auto__chg_field_userprofile_meta"}}, {"pk": 24, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0014_auto__del_courseenrollment"}}, {"pk": 25, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0015_auto__add_courseenrollment__add_unique_courseenrollment_user_course_id"}}, {"pk": 26, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0016_auto__add_field_courseenrollment_date__chg_field_userprofile_country"}}, {"pk": 27, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0017_rename_date_to_created"}}, {"pk": 28, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0018_auto"}}, {"pk": 29, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0019_create_approved_demographic_fields_fall_2012"}}, {"pk": 30, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:34Z", "app_name": "student", "migration": "0020_add_test_center_user"}}, {"pk": 31, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "student", "migration": "0021_remove_askbot"}}, {"pk": 32, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "student", "migration": "0022_auto__add_courseenrollmentallowed__add_unique_courseenrollmentallowed_"}}, {"pk": 33, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "student", "migration": "0023_add_test_center_registration"}}, {"pk": 34, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "student", "migration": "0024_add_allow_certificate"}}, {"pk": 35, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "student", "migration": "0025_auto__add_field_courseenrollmentallowed_auto_enroll"}}, {"pk": 36, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "student", "migration": "0026_auto__remove_index_student_testcenterregistration_accommodation_request"}}, {"pk": 37, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "student", "migration": "0027_add_active_flag_and_mode_to_courseware_enrollment"}}, {"pk": 38, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "student", "migration": "0028_auto__add_userstanding"}}, {"pk": 39, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "student", "migration": "0029_add_lookup_table_between_user_and_anonymous_student_id"}}, {"pk": 40, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "student", "migration": "0029_remove_pearson"}}, {"pk": 41, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "student", "migration": "0030_auto__chg_field_anonymoususerid_anonymous_user_id"}}, {"pk": 42, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "student", "migration": "0031_drop_student_anonymoususerid_temp_archive"}}, {"pk": 43, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:35Z", "app_name": "track", "migration": "0001_initial"}}, {"pk": 44, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "track", "migration": "0002_auto__add_field_trackinglog_host__chg_field_trackinglog_event_type__ch"}}, {"pk": 45, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0001_added_generatedcertificates"}}, {"pk": 46, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0002_auto__add_field_generatedcertificate_download_url"}}, {"pk": 47, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0003_auto__add_field_generatedcertificate_enabled"}}, {"pk": 48, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0004_auto__add_field_generatedcertificate_graded_certificate_id__add_field_"}}, {"pk": 49, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0005_auto__add_field_generatedcertificate_name"}}, {"pk": 50, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0006_auto__chg_field_generatedcertificate_certificate_id"}}, {"pk": 51, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0007_auto__add_revokedcertificate"}}, {"pk": 52, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0008_auto__del_revokedcertificate__del_field_generatedcertificate_name__add"}}, {"pk": 53, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0009_auto__del_field_generatedcertificate_graded_download_url__del_field_ge"}}, {"pk": 54, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0010_auto__del_field_generatedcertificate_enabled__add_field_generatedcerti"}}, {"pk": 55, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0011_auto__del_field_generatedcertificate_certificate_id__add_field_generat"}}, {"pk": 56, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0012_auto__add_field_generatedcertificate_name__add_field_generatedcertific"}}, {"pk": 57, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0013_auto__add_field_generatedcertificate_error_reason"}}, {"pk": 58, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0014_adding_whitelist"}}, {"pk": 59, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:36Z", "app_name": "certificates", "migration": "0015_adding_mode_for_verified_certs"}}, {"pk": 60, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:37Z", "app_name": "instructor_task", "migration": "0001_initial"}}, {"pk": 61, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:37Z", "app_name": "instructor_task", "migration": "0002_add_subtask_field"}}, {"pk": 62, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:37Z", "app_name": "licenses", "migration": "0001_initial"}}, {"pk": 63, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:37Z", "app_name": "bulk_email", "migration": "0001_initial"}}, {"pk": 64, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:37Z", "app_name": "bulk_email", "migration": "0002_change_field_names"}}, {"pk": 65, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:37Z", "app_name": "bulk_email", "migration": "0003_add_optout_user"}}, {"pk": 66, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:37Z", "app_name": "bulk_email", "migration": "0004_migrate_optout_user"}}, {"pk": 67, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:37Z", "app_name": "bulk_email", "migration": "0005_remove_optout_email"}}, {"pk": 68, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:37Z", "app_name": "bulk_email", "migration": "0006_add_course_email_template"}}, {"pk": 69, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:37Z", "app_name": "bulk_email", "migration": "0007_load_course_email_template"}}, {"pk": 70, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:37Z", "app_name": "bulk_email", "migration": "0008_add_course_authorizations"}}, {"pk": 71, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:38Z", "app_name": "external_auth", "migration": "0001_initial"}}, {"pk": 72, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:38Z", "app_name": "wiki", "migration": "0001_initial"}}, {"pk": 73, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:38Z", "app_name": "wiki", "migration": "0002_auto__add_field_articleplugin_created"}}, {"pk": 74, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:38Z", "app_name": "wiki", "migration": "0003_auto__add_field_urlpath_article"}}, {"pk": 75, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:38Z", "app_name": "wiki", "migration": "0004_populate_urlpath__article"}}, {"pk": 76, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:38Z", "app_name": "wiki", "migration": "0005_auto__chg_field_urlpath_article"}}, {"pk": 77, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:39Z", "app_name": "wiki", "migration": "0006_auto__add_attachmentrevision__add_image__add_attachment"}}, {"pk": 78, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:39Z", "app_name": "wiki", "migration": "0007_auto__add_articlesubscription"}}, {"pk": 79, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:39Z", "app_name": "wiki", "migration": "0008_auto__add_simpleplugin__add_revisionpluginrevision__add_imagerevision_"}}, {"pk": 80, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:39Z", "app_name": "wiki", "migration": "0009_auto__add_field_imagerevision_width__add_field_imagerevision_height"}}, {"pk": 81, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:39Z", "app_name": "wiki", "migration": "0010_auto__chg_field_imagerevision_image"}}, {"pk": 82, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:39Z", "app_name": "wiki", "migration": "0011_auto__chg_field_imagerevision_width__chg_field_imagerevision_height"}}, {"pk": 83, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:39Z", "app_name": "django_notify", "migration": "0001_initial"}}, {"pk": 84, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:39Z", "app_name": "notifications", "migration": "0001_initial"}}, {"pk": 85, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:40Z", "app_name": "foldit", "migration": "0001_initial"}}, {"pk": 86, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:40Z", "app_name": "waffle", "migration": "0001_initial"}}, {"pk": 87, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:40Z", "app_name": "waffle", "migration": "0002_auto__add_sample"}}, {"pk": 88, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:40Z", "app_name": "waffle", "migration": "0003_auto__add_field_flag_note__add_field_switch_note__add_field_sample_not"}}, {"pk": 89, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:40Z", "app_name": "waffle", "migration": "0004_auto__add_field_flag_testing"}}, {"pk": 90, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:40Z", "app_name": "waffle", "migration": "0005_auto__add_field_flag_created__add_field_flag_modified"}}, {"pk": 91, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:40Z", "app_name": "waffle", "migration": "0006_auto__add_field_switch_created__add_field_switch_modified__add_field_s"}}, {"pk": 92, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:40Z", "app_name": "waffle", "migration": "0007_auto__chg_field_flag_created__chg_field_flag_modified__chg_field_switc"}}, {"pk": 93, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:40Z", "app_name": "waffle", "migration": "0008_auto__add_field_flag_languages"}}, {"pk": 94, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:41Z", "app_name": "django_comment_client", "migration": "0001_initial"}}, {"pk": 95, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:41Z", "app_name": "django_comment_common", "migration": "0001_initial"}}, {"pk": 96, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:41Z", "app_name": "notes", "migration": "0001_initial"}}, {"pk": 97, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:41Z", "app_name": "user_api", "migration": "0001_initial"}}, {"pk": 98, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:42Z", "app_name": "course_modes", "migration": "0001_initial"}}, {"pk": 99, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:42Z", "app_name": "course_modes", "migration": "0002_auto__add_field_coursemode_currency"}}, {"pk": 100, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:42Z", "app_name": "course_modes", "migration": "0003_auto__add_unique_coursemode_course_id_currency_mode_slug"}}, {"pk": 101, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:42Z", "app_name": "course_modes", "migration": "0004_auto__add_field_coursemode_expiration_date"}}, {"pk": 102, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:42Z", "app_name": "course_modes", "migration": "0005_auto__add_field_coursemode_expiration_datetime"}}, {"pk": 103, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:42Z", "app_name": "course_modes", "migration": "0006_expiration_date_to_datetime"}}, {"pk": 104, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:42Z", "app_name": "verify_student", "migration": "0001_initial"}}, {"pk": 105, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:42Z", "app_name": "linkedin", "migration": "0001_initial"}}, {"pk": 106, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:42Z", "app_name": "django_extensions", "migration": "0001_empty"}}, {"pk": 107, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:42Z", "app_name": "shoppingcart", "migration": "0001_initial"}}, {"pk": 108, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:43Z", "app_name": "shoppingcart", "migration": "0002_auto__add_field_paidcourseregistration_mode"}}, {"pk": 109, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:43Z", "app_name": "shoppingcart", "migration": "0003_auto__del_field_orderitem_line_cost"}}, {"pk": 110, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:43Z", "app_name": "shoppingcart", "migration": "0004_auto__add_field_orderitem_fulfilled_time"}}, {"pk": 111, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:43Z", "app_name": "shoppingcart", "migration": "0005_auto__add_paidcourseregistrationannotation__add_field_orderitem_report"}}, {"pk": 112, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:43Z", "app_name": "shoppingcart", "migration": "0006_auto__add_field_order_refunded_time__add_field_orderitem_refund_reques"}}, {"pk": 113, "model": "south.migrationhistory", "fields": {"applied": "2014-01-25T15:57:43Z", "app_name": "shoppingcart", "migration": "0007_auto__add_field_orderitem_service_fee"}}, {"pk": 1, "model": "bulk_email.courseemailtemplate", "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 account settings at {account_settings_url}.\r\n", "html_template": "
Update from {course_title}