diff --git a/cms/envs/common.py b/cms/envs/common.py index 577692ba00..902bd59c1e 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -81,6 +81,9 @@ FEATURES = { # Hide any Personally Identifiable Information from application logs 'SQUELCH_PII_IN_LOGS': False, + + # Toggles embargo functionality + 'EMBARGO': False, } ENABLE_JASMINE = False @@ -99,6 +102,9 @@ sys.path.append(PROJECT_ROOT / 'djangoapps') sys.path.append(COMMON_ROOT / 'djangoapps') sys.path.append(COMMON_ROOT / 'lib') +# For geolocation ip database +GEOIP_PATH = REPO_ROOT / "common/static/data/geoip/GeoIP.dat" + ############################# WEB CONFIGURATION ############################# # This is where we stick our compiled template files. @@ -185,6 +191,8 @@ MIDDLEWARE_CLASSES = ( # Allows us to dark-launch particular languages 'dark_lang.middleware.DarkLangMiddleware', + 'embargo.middleware.EmbargoMiddleware', + # Detects user-requested locale from 'accept-language' header in http request 'django.middleware.locale.LocaleMiddleware', @@ -467,6 +475,8 @@ INSTALLED_APPS = ( # User preferences 'user_api', 'django_openid_auth', + + 'embargo', ) diff --git a/cms/envs/test.py b/cms/envs/test.py index 37dec4f2ec..1e1adf49e4 100644 --- a/cms/envs/test.py +++ b/cms/envs/test.py @@ -196,3 +196,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/cms/urls.py b/cms/urls.py index 371b20ac5c..65d19e83f7 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -54,6 +54,7 @@ urlpatterns += patterns( # ajax view that actually does the work url(r'^login_post$', 'student.views.login_user', name='login_post'), url(r'^logout$', 'student.views.logout_user', name='logout'), + url(r'^embargo$', 'student.views.embargo', name="embargo"), ) # restful api diff --git a/common/djangoapps/embargo/__init__.py b/common/djangoapps/embargo/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/djangoapps/embargo/admin.py b/common/djangoapps/embargo/admin.py new file mode 100644 index 0000000000..8ff4cc3970 --- /dev/null +++ b/common/djangoapps/embargo/admin.py @@ -0,0 +1,64 @@ +""" +Django admin page for embargo models +""" +from django.contrib import admin +import textwrap + +from config_models.admin import ConfigurationModelAdmin +from embargo.models import EmbargoedCourse, EmbargoedState, IPFilter +from embargo.forms import EmbargoedCourseForm, EmbargoedStateForm, IPFilterForm + + +class EmbargoedCourseAdmin(admin.ModelAdmin): + """Admin for embargoed course ids""" + form = EmbargoedCourseForm + 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. + + To enable embargos against this course (restrict course access from embargoed + states), check the "Embargoed" box, then click "Save". + """) + }), + ) + + +class EmbargoedStateAdmin(ConfigurationModelAdmin): + """Admin for embargoed countries""" + form = EmbargoedStateForm + fieldsets = ( + (None, { + 'fields': ('embargoed_countries',), + 'description': textwrap.dedent("""Enter the two-letter ISO-3166-1 Alpha-2 + code of the country or countries to embargo in the following box. For help, + see + this list of ISO-3166-1 country codes. + + Enter the embargoed country codes separated by a comma. Do not surround with quotes. + """) + }), + ) + + +class IPFilterAdmin(ConfigurationModelAdmin): + """Admin for blacklisting/whitelisting specific IP addresses""" + form = IPFilterForm + fieldsets = ( + (None, { + 'fields': ('whitelist', 'blacklist'), + 'description': textwrap.dedent("""Enter specific IP addresses to explicitly + whitelist (not block) or blacklist (block) in the appropriate box below. + Separate IP addresses with a comma. Do not surround with quotes. + """) + }), + ) + +admin.site.register(EmbargoedCourse, EmbargoedCourseAdmin) +admin.site.register(EmbargoedState, EmbargoedStateAdmin) +admin.site.register(IPFilter, IPFilterAdmin) diff --git a/common/djangoapps/embargo/fixtures/__init__.py b/common/djangoapps/embargo/fixtures/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/djangoapps/embargo/fixtures/country_codes.py b/common/djangoapps/embargo/fixtures/country_codes.py new file mode 100644 index 0000000000..cc73615a26 --- /dev/null +++ b/common/djangoapps/embargo/fixtures/country_codes.py @@ -0,0 +1,25 @@ +""" +List of valid ISO 3166-1 Alpha-2 country codes, used for +validating entries on entered country codes on django-admin page. +""" + +COUNTRY_CODES = set([ + "AC", "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN", "AO", "AQ", "AR", "AS", "AT", + "AU", "AW", "AX", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BM", + "BN", "BO", "BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", + "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", "CU", "CV", "CX", "CY", "CZ", "DE", + "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "ER", "ES", "ET", "FI", "FJ", "FK", + "FM", "FO", "FR", "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", "GM", "GN", + "GP", "GQ", "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", + "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", "IT", "JE", "JM", "JO", "JP", + "KE", "KG", "KH", "KI", "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", + "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "ME", "MG", "MH", + "MK", "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX", + "MY", "MZ", "NA", "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", "NU", "NZ", + "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR", "PT", "PW", "PY", + "QA", "RE", "RO", "RS", "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", + "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV", "SY", "SZ", "TA", "TC", "TD", + "TF", "TG", "TH", "TJ", "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", "TW", "TZ", + "UA", "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", + "WS", "YE", "YT", "ZA", "ZM", "ZW" +]) diff --git a/common/djangoapps/embargo/forms.py b/common/djangoapps/embargo/forms.py new file mode 100644 index 0000000000..09ea11445b --- /dev/null +++ b/common/djangoapps/embargo/forms.py @@ -0,0 +1,125 @@ +""" +Defines forms for providing validation of embargo admin details. +""" + +from django import forms + +from embargo.models import EmbargoedCourse, EmbargoedState, IPFilter +from embargo.fixtures.country_codes import COUNTRY_CODES + +import socket + +from xmodule.modulestore.django import modulestore + + +class EmbargoedCourseForm(forms.ModelForm): # pylint: disable=incomplete-protocol + """Form providing validation of entered Course IDs.""" + + class Meta: # pylint: disable=missing-docstring + model = EmbargoedCourse + + 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: + 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) + 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) + + return course_id + + +class EmbargoedStateForm(forms.ModelForm): # pylint: disable=incomplete-protocol + """Form validating entry of states to embargo""" + + class Meta: # pylint: disable=missing-docstring + model = EmbargoedState + + def _is_valid_code(self, code): + """Whether or not code is a valid country code""" + return code in COUNTRY_CODES + + def clean_embargoed_countries(self): + """Validate the country list""" + embargoed_countries = self.cleaned_data["embargoed_countries"] + if not embargoed_countries: + return '' + + error_countries = [] + + for country in embargoed_countries.split(','): + country = country.strip().upper() + if not self._is_valid_code(country): + error_countries.append(country) + + if error_countries: + msg = 'COULD NOT PARSE COUNTRY CODE(S) FOR: {0}'.format(error_countries) + msg += ' Please check the list of country codes and verify your entries.' + raise forms.ValidationError(msg) + + return embargoed_countries + + +class IPFilterForm(forms.ModelForm): # pylint: disable=incomplete-protocol + """Form validating entry of IP addresses""" + + class Meta: # pylint: disable=missing-docstring + model = IPFilter + + def _is_valid_ipv4(self, address): + """Whether or not address is a valid ipv4 address""" + try: + # Is this an ipv4 address? + socket.inet_pton(socket.AF_INET, address) + except socket.error: + return False + return True + + def _is_valid_ipv6(self, address): + """Whether or not address is a valid ipv6 address""" + try: + # Is this an ipv6 address? + socket.inet_pton(socket.AF_INET6, address) + except socket.error: + return False + return True + + def _valid_ip_addresses(self, addresses): + """ + Checks if a csv string of IP addresses contains valid values. + + If not, raises a ValidationError. + """ + if addresses == '': + return '' + error_addresses = [] + for addr in addresses.split(','): + address = addr.strip() + if not (self._is_valid_ipv4(address) or self._is_valid_ipv6(address)): + error_addresses.append(address) + if error_addresses: + msg = 'Invalid IP Address(es): {0}'.format(error_addresses) + msg += ' Please fix the error(s) and try again.' + raise forms.ValidationError(msg) + + return addresses + + def clean_whitelist(self): + """Validates the whitelist""" + whitelist = self.cleaned_data["whitelist"] + return self._valid_ip_addresses(whitelist) + + def clean_blacklist(self): + """Validates the blacklist""" + blacklist = self.cleaned_data["blacklist"] + return self._valid_ip_addresses(blacklist) diff --git a/common/djangoapps/embargo/middleware.py b/common/djangoapps/embargo/middleware.py new file mode 100644 index 0000000000..2e2cb2b645 --- /dev/null +++ b/common/djangoapps/embargo/middleware.py @@ -0,0 +1,54 @@ +""" +Middleware for embargoing courses. + +IMPORTANT NOTE: This code WILL NOT WORK if you have a misconfigured proxy +server. If you are configuring embargo functionality, or if you are +experiencing mysterious problems with embargoing, please check that your +reverse proxy is setting any of the well known client IP address headers (ex., +HTTP_X_FORWARDED_FOR). +""" + +import pygeoip + +from django.core.exceptions import MiddlewareNotUsed +from django.conf import settings +from django.shortcuts import redirect +from ipware.ip import get_ip +from util.request import course_id_from_url + +from embargo.models import EmbargoedCourse, EmbargoedState, IPFilter + + +class EmbargoMiddleware(object): + """ + Middleware for embargoing courses + + This is configured by creating ``EmbargoedCourse``, ``EmbargoedState``, and + optionally ``IPFilter`` rows in the database, using the django admin site. + """ + def __init__(self): + # If embargoing is turned off, make this middleware do nothing + if not settings.FEATURES.get('EMBARGO', False): + raise MiddlewareNotUsed() + + def process_request(self, request): + """ + Processes embargo requests + """ + url = request.path + course_id = course_id_from_url(url) + + # If they're trying to access a course that cares about embargoes + if EmbargoedCourse.is_embargoed(course_id): + # If we're having performance issues, add caching here + ip_addr = get_ip(request) + + # if blacklisted, immediately fail + if ip_addr in IPFilter.current().blacklist_ips: + return redirect('embargo') + + country_code_from_ip = pygeoip.GeoIP(settings.GEOIP_PATH).country_code_by_addr(ip_addr) + is_embargoed = country_code_from_ip in EmbargoedState.current().embargoed_countries_list + # Fail if country is embargoed and the ip address isn't explicitly whitelisted + if is_embargoed and ip_addr not in IPFilter.current().whitelist_ips: + return redirect('embargo') diff --git a/common/djangoapps/embargo/migrations/0001_initial.py b/common/djangoapps/embargo/migrations/0001_initial.py new file mode 100644 index 0000000000..9516b8985e --- /dev/null +++ b/common/djangoapps/embargo/migrations/0001_initial.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'EmbargoedCourse' + db.create_table('embargo_embargoedcourse', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('course_id', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255, db_index=True)), + ('embargoed', self.gf('django.db.models.fields.BooleanField')(default=False)), + )) + db.send_create_signal('embargo', ['EmbargoedCourse']) + + # Adding model 'EmbargoedState' + db.create_table('embargo_embargoedstate', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('change_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('changed_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.PROTECT)), + ('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('embargoed_countries', self.gf('django.db.models.fields.TextField')(blank=True)), + )) + db.send_create_signal('embargo', ['EmbargoedState']) + + # Adding model 'IPFilter' + db.create_table('embargo_ipfilter', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('change_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('changed_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.PROTECT)), + ('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('whitelist', self.gf('django.db.models.fields.TextField')(blank=True)), + ('blacklist', self.gf('django.db.models.fields.TextField')(blank=True)), + )) + db.send_create_signal('embargo', ['IPFilter']) + + + def backwards(self, orm): + # Deleting model 'EmbargoedCourse' + db.delete_table('embargo_embargoedcourse') + + # Deleting model 'EmbargoedState' + db.delete_table('embargo_embargoedstate') + + # Deleting model 'IPFilter' + db.delete_table('embargo_ipfilter') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'embargo.embargoedcourse': { + 'Meta': {'object_name': 'EmbargoedCourse'}, + 'course_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}), + 'embargoed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'embargo.embargoedstate': { + 'Meta': {'object_name': 'EmbargoedState'}, + 'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}), + 'embargoed_countries': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'embargo.ipfilter': { + 'Meta': {'object_name': 'IPFilter'}, + 'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + } + } + + complete_apps = ['embargo'] \ No newline at end of file diff --git a/common/djangoapps/embargo/migrations/__init__.py b/common/djangoapps/embargo/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/djangoapps/embargo/models.py b/common/djangoapps/embargo/models.py new file mode 100644 index 0000000000..4ee6613859 --- /dev/null +++ b/common/djangoapps/embargo/models.py @@ -0,0 +1,98 @@ +""" +Models for embargoing visits to certain courses by IP address. + +WE'RE USING MIGRATIONS! + +If you make changes to this model, be sure to create an appropriate migration +file and check it in at the same time as your model changes. To do that, + +1. Go to the edx-platform dir +2. ./manage.py lms schemamigration embargo --auto description_of_your_change +3. Add the migration file created in edx-platform/common/djangoapps/embargo/migrations/ +""" +from django.db import models + +from config_models.models import ConfigurationModel + + +class EmbargoedCourse(models.Model): + """ + Enable course embargo on a course-by-course basis. + """ + # The course to embargo + course_id = models.CharField(max_length=255, db_index=True, unique=True) + + # Whether or not to embargo + embargoed = models.BooleanField(default=False) + + @classmethod + def is_embargoed(cls, course_id): + """ + Returns whether or not the given course id is embargoed. + + If course has not been explicitly embargoed, returns False. + """ + try: + record = cls.objects.get(course_id=course_id) + return record.embargoed + except cls.DoesNotExist: + return False + + def __unicode__(self): + not_em = "Not " + if self.embargoed: + not_em = "" + return u"Course '{}' is {}Embargoed".format(self.course_id, not_em) + + +class EmbargoedState(ConfigurationModel): + """ + Register countries to be embargoed. + """ + # The countries to embargo + embargoed_countries = models.TextField( + blank=True, + help_text="A comma-separated list of country codes that fall under U.S. embargo restrictions" + ) + + @property + def embargoed_countries_list(self): + """ + Return a list of upper case country codes + """ + if self.embargoed_countries == '': + return [] + return [country.strip().upper() for country in self.embargoed_countries.split(',')] # pylint: disable=no-member + + +class IPFilter(ConfigurationModel): + """ + Register specific IP addresses to explicitly block or unblock. + """ + whitelist = models.TextField( + blank=True, + help_text="A comma-separated list of IP addresses that should not fall under embargo restrictions." + ) + + blacklist = models.TextField( + blank=True, + help_text="A comma-separated list of IP addresses that should fall under embargo restrictions." + ) + + @property + def whitelist_ips(self): + """ + Return a list of valid IP addresses to whitelist + """ + if self.whitelist == '': + return [] + return [addr.strip() for addr in self.whitelist.split(',')] # pylint: disable=no-member + + @property + def blacklist_ips(self): + """ + Return a list of valid IP addresses to blacklist + """ + if self.blacklist == '': + return [] + return [addr.strip() for addr in self.blacklist.split(',')] # pylint: disable=no-member diff --git a/common/djangoapps/embargo/tests/test_forms.py b/common/djangoapps/embargo/tests/test_forms.py new file mode 100644 index 0000000000..cea030c23d --- /dev/null +++ b/common/djangoapps/embargo/tests/test_forms.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- +""" +Unit tests for embargo app admin forms. +""" + +from django.test import TestCase +from django.test.utils import override_settings + +# Explicitly import the cache from ConfigurationModel so we can reset it after each test +from config_models.models import cache +from embargo.forms import EmbargoedCourseForm, EmbargoedStateForm, IPFilterForm +from embargo.models import EmbargoedCourse, EmbargoedState, IPFilter + +from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase +from xmodule.modulestore.tests.factories import CourseFactory +from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE + + +@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE) +class EmbargoCourseFormTest(ModuleStoreTestCase): + """Test the course form properly validates course IDs""" + + def setUp(self): + self.course = CourseFactory.create() + self.true_form_data = {'course_id': self.course.id, 'embargoed': True} + self.false_form_data = {'course_id': self.course.id, 'embargoed': False} + + def test_embargo_course(self): + self.assertFalse(EmbargoedCourse.is_embargoed(self.course.id)) + # Test adding embargo to this course + form = EmbargoedCourseForm(data=self.true_form_data) + # Validation should work + self.assertTrue(form.is_valid()) + form.save() + # Check that this course is embargoed + self.assertTrue(EmbargoedCourse.is_embargoed(self.course.id)) + + def test_repeat_course(self): + # Initially course shouldn't be authorized + self.assertFalse(EmbargoedCourse.is_embargoed(self.course.id)) + # Test authorizing the course, which should totally work + form = EmbargoedCourseForm(data=self.true_form_data) + # Validation should work + self.assertTrue(form.is_valid()) + form.save() + # Check that this course is authorized + self.assertTrue(EmbargoedCourse.is_embargoed(self.course.id)) + + # Now make a new course authorization with the same course id that tries to turn email off + form = EmbargoedCourseForm(data=self.false_form_data) + # Validation should not work because course_id field is unique + self.assertFalse(form.is_valid()) + self.assertEquals( + "Embargoed course with this Course id already exists.", + form._errors['course_id'][0] # pylint: disable=protected-access + ) + with self.assertRaisesRegexp(ValueError, "The EmbargoedCourse could not be created because the data didn't validate."): + form.save() + + # Course should still be authorized (invalid attempt had no effect) + self.assertTrue(EmbargoedCourse.is_embargoed(self.course.id)) + + def test_form_typo(self): + # Munge course id + bad_id = self.course.id + '_typo' + + form_data = {'course_id': bad_id, 'embargoed': True} + form = EmbargoedCourseForm(data=form_data) + # Validation shouldn't work + self.assertFalse(form.is_valid()) + + 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 + + with self.assertRaisesRegexp(ValueError, "The EmbargoedCourse could not be created because the data didn't validate."): + form.save() + + def test_invalid_location(self): + # Munge course id + bad_id = self.course.id.split('/')[-1] + + form_data = {'course_id': bad_id, 'embargoed': True} + form = EmbargoedCourseForm(data=form_data) + # Validation shouldn't work + self.assertFalse(form.is_valid()) + + 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 + + with self.assertRaisesRegexp(ValueError, "The EmbargoedCourse could not be created because the data didn't validate."): + form.save() + + +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 + cache.clear() + + def test_add_valid_states(self): + # test adding valid two letter states + # case and spacing should not matter + form_data = {'embargoed_countries': 'cu, Sy , US'} + form = EmbargoedStateForm(data=form_data) + self.assertTrue(form.is_valid()) + form.save() + current_embargoes = EmbargoedState.current().embargoed_countries_list + for country in ["CU", "SY", "US"]: + self.assertIn(country, current_embargoes) + # Test clearing by adding an empty list is OK too + form_data = {'embargoed_countries': ''} + form = EmbargoedStateForm(data=form_data) + self.assertTrue(form.is_valid()) + 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 + # usa is not valid + form_data = {'embargoed_countries': 'usa, xx'} + form = EmbargoedStateForm(data=form_data) + self.assertFalse(form.is_valid()) + + msg = 'COULD NOT PARSE COUNTRY CODE(S) FOR: {0}'.format([u'USA', u'XX']) + msg += ' Please check the list of country codes and verify your entries.' + self.assertEquals(msg, form._errors['embargoed_countries'][0]) # pylint: disable=protected-access + + with self.assertRaisesRegexp(ValueError, "The EmbargoedState could not be created because the data didn't validate."): + form.save() + + self.assertFalse('USA' in EmbargoedState.current().embargoed_countries_list) + self.assertFalse('XX' in EmbargoedState.current().embargoed_countries_list) + + +class IPFilterFormTest(TestCase): + """Test form for adding [black|white]list IP addresses""" + + def tearDown(self): + # Explicitly clear ConfigurationModel's cache so tests have a clear cache + # and don't interfere with each other + cache.clear() + + def test_add_valid_ips(self): + # test adding valid ip addresses + # should be able to do both ipv4 and ipv6 + # spacing should not matter + form_data = { + 'whitelist': '127.0.0.1, 2003:dead:beef:4dad:23:46:bb:101', + 'blacklist': ' 18.244.1.5 , 2002:c0a8:101::42, 18.36.22.1' + } + form = IPFilterForm(data=form_data) + self.assertTrue(form.is_valid()) + form.save() + whitelist = IPFilter.current().whitelist_ips + blacklist = IPFilter.current().blacklist_ips + for addr in '127.0.0.1, 2003:dead:beef:4dad:23:46:bb:101'.split(','): + self.assertIn(addr.strip(), whitelist) + for addr in '18.244.1.5, 2002:c0a8:101::42, 18.36.22.1'.split(','): + self.assertIn(addr.strip(), blacklist) + + # Test clearing by adding an empty list is OK too + form_data = { + 'whitelist': '', + 'blacklist': '' + } + form = IPFilterForm(data=form_data) + self.assertTrue(form.is_valid()) + form.save() + self.assertTrue(len(IPFilter.current().whitelist) == 0) + self.assertTrue(len(IPFilter.current().blacklist) == 0) + + def test_add_invalid_ips(self): + # test adding invalid ip addresses + form_data = { + 'whitelist': '.0.0.1, :dead:beef:::', + 'blacklist': ' 18.244.* , 999999:c0a8:101::42' + } + form = IPFilterForm(data=form_data) + self.assertFalse(form.is_valid()) + + wmsg = "Invalid IP Address(es): [u'.0.0.1', u':dead:beef:::'] Please fix the error(s) and try again." + self.assertEquals(wmsg, form._errors['whitelist'][0]) # pylint: disable=protected-access + bmsg = "Invalid IP Address(es): [u'18.244.*', u'999999:c0a8:101::42'] Please fix the error(s) and try again." + self.assertEquals(bmsg, form._errors['blacklist'][0]) # pylint: disable=protected-access + + with self.assertRaisesRegexp(ValueError, "The IPFilter could not be created because the data didn't validate."): + form.save() diff --git a/common/djangoapps/embargo/tests/test_middleware.py b/common/djangoapps/embargo/tests/test_middleware.py new file mode 100644 index 0000000000..c3f418c905 --- /dev/null +++ b/common/djangoapps/embargo/tests/test_middleware.py @@ -0,0 +1,159 @@ +""" +Tests for EmbargoMiddleware +""" + +import mock +import pygeoip +import unittest + +from django.conf import settings +from django.test import TestCase, Client +from django.test.utils import override_settings +from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE +from student.models import CourseEnrollment +from student.tests.factories import UserFactory +from xmodule.modulestore.tests.factories import CourseFactory + +# Explicitly import the cache from ConfigurationModel so we can reset it after each test +from config_models.models import cache +from embargo.models import EmbargoedCourse, EmbargoedState, IPFilter + + +@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE) +class EmbargoMiddlewareTests(TestCase): + """ + Tests of EmbargoMiddleware + """ + def setUp(self): + self.client = Client() + self.user = UserFactory(username='fred', password='secret') + self.client.login(username='fred', password='secret') + self.embargo_course = CourseFactory.create() + self.embargo_course.save() + self.regular_course = CourseFactory.create(org="Regular") + self.regular_course.save() + self.embargoed_page = '/courses/' + self.embargo_course.id + '/info' + self.regular_page = '/courses/' + self.regular_course.id + '/info' + EmbargoedCourse(course_id=self.embargo_course.id, embargoed=True).save() + EmbargoedState( + embargoed_countries="cu, ir, Sy, SD", + changed_by=self.user, + enabled=True + ).save() + CourseEnrollment.enroll(self.user, self.regular_course.id) + CourseEnrollment.enroll(self.user, self.embargo_course.id) + # 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): + """ + Gives us a fake set of IPs + """ + ip_dict = { + '1.0.0.0': 'CU', + '2.0.0.0': 'IR', + '3.0.0.0': 'SY', + '4.0.0.0': 'SD', + '5.0.0.0': 'AQ', # Antartica + } + return ip_dict.get(ip_addr, 'US') + + @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') + def test_countries(self): + # 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 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) + + @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') + def test_ip_exceptions(self): + # 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 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 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) + + @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/djangoapps/embargo/tests/test_models.py b/common/djangoapps/embargo/tests/test_models.py new file mode 100644 index 0000000000..12c66295b8 --- /dev/null +++ b/common/djangoapps/embargo/tests/test_models.py @@ -0,0 +1,80 @@ +"""Test of models for embargo middleware app""" +from django.test import TestCase + +from embargo.models import EmbargoedCourse, EmbargoedState, IPFilter + + +class EmbargoModelsTest(TestCase): + """Test each of the 3 models in embargo.models""" + def test_course_embargo(self): + course_id = 'abc/123/doremi' + # Test that course is not authorized by default + self.assertFalse(EmbargoedCourse.is_embargoed(course_id)) + + # Authorize + cauth = EmbargoedCourse(course_id=course_id, embargoed=True) + cauth.save() + + # Now, course should be embargoed + self.assertTrue(EmbargoedCourse.is_embargoed(course_id)) + self.assertEquals( + cauth.__unicode__(), + "Course 'abc/123/doremi' is Embargoed" + ) + + # Unauthorize by explicitly setting email_enabled to False + cauth.embargoed = False + cauth.save() + # Test that course is now unauthorized + self.assertFalse(EmbargoedCourse.is_embargoed(course_id)) + self.assertEquals( + cauth.__unicode__(), + "Course 'abc/123/doremi' is Not Embargoed" + ) + + def test_state_embargo(self): + # Azerbaijan and France should not be blocked + good_states = ['AZ', 'FR'] + # Gah block USA and Antartica + blocked_states = ['US', 'AQ'] + currently_blocked = EmbargoedState.current().embargoed_countries_list + + for state in blocked_states + good_states: + self.assertFalse(state in currently_blocked) + + # Block + cauth = EmbargoedState(embargoed_countries='US, AQ') + cauth.save() + currently_blocked = EmbargoedState.current().embargoed_countries_list + + for state in good_states: + self.assertFalse(state in currently_blocked) + for state in blocked_states: + self.assertTrue(state in currently_blocked) + + # Change embargo - block Isle of Man too + blocked_states.append('IM') + cauth.embargoed_countries = 'US, AQ, IM' + cauth.save() + currently_blocked = EmbargoedState.current().embargoed_countries_list + + for state in good_states: + self.assertFalse(state in currently_blocked) + for state in blocked_states: + self.assertTrue(state in currently_blocked) + + def test_ip_blocking(self): + whitelist = '127.0.0.1' + blacklist = '18.244.51.3' + + cwhitelist = IPFilter.current().whitelist_ips + self.assertFalse(whitelist in cwhitelist) + cblacklist = IPFilter.current().blacklist_ips + self.assertFalse(blacklist in cblacklist) + + IPFilter(whitelist=whitelist, blacklist=blacklist).save() + + cwhitelist = IPFilter.current().whitelist_ips + self.assertTrue(whitelist in cwhitelist) + cblacklist = IPFilter.current().blacklist_ips + self.assertTrue(blacklist in cblacklist) diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index 4dff62cf80..453ea03ca3 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -142,6 +142,15 @@ def _get_date_for_press(publish_date): return date +def embargo(_request): + """ + Render the embargo page. + + Explains to the user why they are not able to access a particular embargoed course. + """ + return render_to_response('static_templates/embargo.html') + + def press(request): json_articles = cache.get("student_press_json_articles") if json_articles is None: @@ -723,7 +732,7 @@ def login_user(request, error=""): # This is actually the common case, logging in user without external linked login AUDIT_LOG.info("User %s w/o external auth attempting login", user) - # see if account has been locked out due to excessive login failres + # see if account has been locked out due to excessive login failures user_found_by_email_lookup = user if user_found_by_email_lookup and LoginFailures.is_feature_enabled(): if LoginFailures.is_user_locked_out(user_found_by_email_lookup): diff --git a/common/djangoapps/track/contexts.py b/common/djangoapps/track/contexts.py index 0fb06fb1b1..070ac10ebd 100644 --- a/common/djangoapps/track/contexts.py +++ b/common/djangoapps/track/contexts.py @@ -1,12 +1,9 @@ """Generates common contexts""" - -import re import logging from xmodule.course_module import CourseDescriptor +from util.request import COURSE_REGEX - -COURSE_REGEX = re.compile(r'^.*?/courses/(?P[^/]+/[^/]+/[^/]+)') log = logging.getLogger(__name__) diff --git a/common/djangoapps/util/request.py b/common/djangoapps/util/request.py index fc9c835194..a26059e8a7 100644 --- a/common/djangoapps/util/request.py +++ b/common/djangoapps/util/request.py @@ -1,7 +1,11 @@ """ Utility functions related to HTTP requests """ +import re + from django.conf import settings from microsite_configuration.middleware import MicrositeConfiguration +COURSE_REGEX = re.compile(r'^.*?/courses/(?P[^/]+/[^/]+/[^/]+)') + def safe_get_host(request): """ @@ -16,3 +20,17 @@ def safe_get_host(request): return request.get_host() else: return MicrositeConfiguration.get_microsite_configuration_value('site_domain', settings.SITE_NAME) + + +def course_id_from_url(url): + """ + Extracts the course_id from the given `url`. + """ + url = url or '' + + match = COURSE_REGEX.match(url) + course_id = '' + if match: + course_id = match.group('course_id') or '' + + return course_id diff --git a/common/lib/xmodule/xmodule/peer_grading_module.py b/common/lib/xmodule/xmodule/peer_grading_module.py index b16ca81656..11c063e545 100644 --- a/common/lib/xmodule/xmodule/peer_grading_module.py +++ b/common/lib/xmodule/xmodule/peer_grading_module.py @@ -255,7 +255,7 @@ class PeerGradingModule(PeerGradingFields, XModule): count_graded = self.student_data_for_location['count_graded'] count_required = self.student_data_for_location['count_required'] except: - success, response = self.query_data_for_location(self.location) + success, response = self.query_data_for_location(self.link_to_location) if not success: log.exception( "No instance data found and could not get data from controller for loc {0} student {1}".format( @@ -706,6 +706,7 @@ class PeerGradingDescriptor(PeerGradingFields, RawDescriptor): closed = module_attr('closed') get_instance_state = module_attr('get_instance_state') get_next_submission = module_attr('get_next_submission') + graded = module_attr('graded') is_student_calibrated = module_attr('is_student_calibrated') peer_grading = module_attr('peer_grading') peer_grading_closed = module_attr('peer_grading_closed') @@ -715,4 +716,6 @@ class PeerGradingDescriptor(PeerGradingFields, RawDescriptor): save_calibration_essay = module_attr('save_calibration_essay') save_grade = module_attr('save_grade') show_calibration_essay = module_attr('show_calibration_essay') + use_for_single_location_local = module_attr('use_for_single_location_local') _find_corresponding_module_for_location = module_attr('_find_corresponding_module_for_location') + diff --git a/common/lib/xmodule/xmodule/tests/test_peer_grading.py b/common/lib/xmodule/xmodule/tests/test_peer_grading.py index ebfeaa0693..8445527aea 100644 --- a/common/lib/xmodule/xmodule/tests/test_peer_grading.py +++ b/common/lib/xmodule/xmodule/tests/test_peer_grading.py @@ -1,7 +1,7 @@ import unittest import json import logging -from mock import Mock +from mock import Mock, patch from webob.multidict import MultiDict from xblock.field_data import DictFieldData @@ -78,12 +78,13 @@ class PeerGradingModuleTest(unittest.TestCase, DummyModulestore): success, _data = self.peer_grading.query_data_for_location(self.problem_location.url()) self.assertTrue(success) - def test_get_score(self): + def test_get_score_none(self): """ - Test getting the score - @return: + Test getting the score. """ score = self.peer_grading.get_score() + + # Score should be None. self.assertIsNone(score['score']) def test_get_max_score(self): @@ -179,6 +180,56 @@ class PeerGradingModuleTest(unittest.TestCase, DummyModulestore): ) ) + def test_get_score_success_fails(self): + """ + Test if query_data_for_location not succeed, their score is None. + """ + score_dict = self.get_score(False, 0, 0) + + # Score dict should be None. + self.assertIsNone(score_dict) + + def test_get_score(self): + """ + Test if the student has graded equal to required submissions, + their score is 1.0. + """ + + score_dict = self.get_score(True, 3, 3) + + # Score should be 1.0. + self.assertEqual(score_dict["score"], 1.0) + + # Testing score after data is stored in student_data_for_location in xmodule. + _score_dict = self.peer_grading.get_score() + + # Score should be 1.0. + self.assertEqual(_score_dict["score"], 1.0) + + def test_get_score_zero(self): + """ + Test if the student has graded not equal to required submissions, + their score is 0.0. + """ + score_dict = self.get_score(True, 2, 3) + + # Score should be 0.0. + self.assertEqual(score_dict["score"], 0.0) + + def get_score(self, success, count_graded, count_required): + self.peer_grading.use_for_single_location_local = True + self.peer_grading.graded = True + + # Patch for external grading service. + with patch('xmodule.peer_grading_module.PeerGradingModule.query_data_for_location') as mock_query_data_for_location: + mock_query_data_for_location.return_value = ( + success, + {"count_graded": count_graded, "count_required": count_required} + ) + + # Returning score dict. + return self.peer_grading.get_score() + class MockPeerGradingServiceProblemList(MockPeerGradingService): def get_problem_list(self, course_id, grader_id): diff --git a/common/lib/xmodule/xmodule/video_module/video_module.py b/common/lib/xmodule/xmodule/video_module/video_module.py index e75b7b4c46..172ab8cc33 100644 --- a/common/lib/xmodule/xmodule/video_module/video_module.py +++ b/common/lib/xmodule/xmodule/video_module/video_module.py @@ -245,24 +245,28 @@ class VideoModule(VideoFields, XModule): elif self.sub: track_url = self.runtime.handler_url(self, 'transcript').rstrip('/?') + '/download' - if self.transcript_language in self.transcripts: - transcript_language = self.transcript_language - elif self.sub: + if not self.transcripts: transcript_language = 'en' - elif self.transcripts: - transcript_language = self.transcripts.keys()[0] + languages = {'en': 'English'} else: - # this for the case, when for currently selected video, - # there are no translations and English subtitles are not set by instructor. - transcript_language = 'null' + if self.transcript_language in self.transcripts: + transcript_language = self.transcript_language + elif self.sub: + transcript_language = 'en' + else: + transcript_language = sorted(self.transcripts.keys())[0] - all_languages = {i[0]: i[1] for i in settings.ALL_LANGUAGES} - languages = {lang: all_languages[lang] for lang in self.transcripts} - if self.sub: - languages.update({'en': 'English'}) + languages = { + lang: display + for lang, display in settings.ALL_LANGUAGES + if lang in self.transcripts + } + + if self.sub: + languages['en'] = 'English' # OrderedDict for easy testing of rendered context in tests - transcript_languages = OrderedDict(sorted(languages.items(), key=itemgetter(1))) + sorted_languages = OrderedDict(sorted(languages.items(), key=itemgetter(1))) return self.system.render_template('video.html', { 'ajax_url': self.system.ajax_url + '/save_user_state', @@ -287,7 +291,7 @@ class VideoModule(VideoFields, XModule): 'yt_test_timeout': 1500, 'yt_test_url': settings.YOUTUBE_TEST_URL, 'transcript_language': transcript_language, - 'transcript_languages': json.dumps(transcript_languages), + 'transcript_languages': json.dumps(sorted_languages), 'transcript_translation_url': self.runtime.handler_url(self, 'transcript').rstrip('/?') + '/translation', 'transcript_available_translations_url': self.runtime.handler_url(self, 'transcript').rstrip('/?') + '/available_translations', }) diff --git a/common/static/data/geoip/GeoIP.dat b/common/static/data/geoip/GeoIP.dat new file mode 100644 index 0000000000..5e38e99bdf Binary files /dev/null and b/common/static/data/geoip/GeoIP.dat differ diff --git a/common/static/data/geoip/README b/common/static/data/geoip/README new file mode 100644 index 0000000000..b32fd89783 --- /dev/null +++ b/common/static/data/geoip/README @@ -0,0 +1,2 @@ +This product includes GeoLite data created by MaxMind, available from +http://www.maxmind.com. \ No newline at end of file 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}

edX
Connect with edX:        

{course_title}


{{message_body}}
       
Copyright \u00a9 2013 edX, All rights reserved.


Our mailing address is:
edX
11 Cambridge Center, Suite 101
Cambridge, MA, USA 02142


This email was automatically sent from {platform_name}.
You are receiving this email at address {email} because you are enrolled in {course_title}.
To stop receiving email like this, update your course email settings here.
"}}, {"pk": 64, "model": "auth.permission", "fields": {"codename": "add_logentry", "name": "Can add log entry", "content_type": 22}}, {"pk": 65, "model": "auth.permission", "fields": {"codename": "change_logentry", "name": "Can change log entry", "content_type": 22}}, {"pk": 66, "model": "auth.permission", "fields": {"codename": "delete_logentry", "name": "Can delete log entry", "content_type": 22}}, {"pk": 4, "model": "auth.permission", "fields": {"codename": "add_group", "name": "Can add group", "content_type": 2}}, {"pk": 5, "model": "auth.permission", "fields": {"codename": "change_group", "name": "Can change group", "content_type": 2}}, {"pk": 6, "model": "auth.permission", "fields": {"codename": "delete_group", "name": "Can delete group", "content_type": 2}}, {"pk": 1, "model": "auth.permission", "fields": {"codename": "add_permission", "name": "Can add permission", "content_type": 1}}, {"pk": 2, "model": "auth.permission", "fields": {"codename": "change_permission", "name": "Can change permission", "content_type": 1}}, {"pk": 3, "model": "auth.permission", "fields": {"codename": "delete_permission", "name": "Can delete permission", "content_type": 1}}, {"pk": 7, "model": "auth.permission", "fields": {"codename": "add_user", "name": "Can add user", "content_type": 3}}, {"pk": 8, "model": "auth.permission", "fields": {"codename": "change_user", "name": "Can change user", "content_type": 3}}, {"pk": 9, "model": "auth.permission", "fields": {"codename": "delete_user", "name": "Can delete user", "content_type": 3}}, {"pk": 142, "model": "auth.permission", "fields": {"codename": "add_courseauthorization", "name": "Can add course authorization", "content_type": 48}}, {"pk": 143, "model": "auth.permission", "fields": {"codename": "change_courseauthorization", "name": "Can change course authorization", "content_type": 48}}, {"pk": 144, "model": "auth.permission", "fields": {"codename": "delete_courseauthorization", "name": "Can delete course authorization", "content_type": 48}}, {"pk": 133, "model": "auth.permission", "fields": {"codename": "add_courseemail", "name": "Can add course email", "content_type": 45}}, {"pk": 134, "model": "auth.permission", "fields": {"codename": "change_courseemail", "name": "Can change course email", "content_type": 45}}, {"pk": 135, "model": "auth.permission", "fields": {"codename": "delete_courseemail", "name": "Can delete course email", "content_type": 45}}, {"pk": 139, "model": "auth.permission", "fields": {"codename": "add_courseemailtemplate", "name": "Can add course email template", "content_type": 47}}, {"pk": 140, "model": "auth.permission", "fields": {"codename": "change_courseemailtemplate", "name": "Can change course email template", "content_type": 47}}, {"pk": 141, "model": "auth.permission", "fields": {"codename": "delete_courseemailtemplate", "name": "Can delete course email template", "content_type": 47}}, {"pk": 136, "model": "auth.permission", "fields": {"codename": "add_optout", "name": "Can add optout", "content_type": 46}}, {"pk": 137, "model": "auth.permission", "fields": {"codename": "change_optout", "name": "Can change optout", "content_type": 46}}, {"pk": 138, "model": "auth.permission", "fields": {"codename": "delete_optout", "name": "Can delete optout", "content_type": 46}}, {"pk": 118, "model": "auth.permission", "fields": {"codename": "add_certificatewhitelist", "name": "Can add certificate whitelist", "content_type": 40}}, {"pk": 119, "model": "auth.permission", "fields": {"codename": "change_certificatewhitelist", "name": "Can change certificate whitelist", "content_type": 40}}, {"pk": 120, "model": "auth.permission", "fields": {"codename": "delete_certificatewhitelist", "name": "Can delete certificate whitelist", "content_type": 40}}, {"pk": 121, "model": "auth.permission", "fields": {"codename": "add_generatedcertificate", "name": "Can add generated certificate", "content_type": 41}}, {"pk": 122, "model": "auth.permission", "fields": {"codename": "change_generatedcertificate", "name": "Can change generated certificate", "content_type": 41}}, {"pk": 123, "model": "auth.permission", "fields": {"codename": "delete_generatedcertificate", "name": "Can delete generated certificate", "content_type": 41}}, {"pk": 46, "model": "auth.permission", "fields": {"codename": "add_servercircuit", "name": "Can add server circuit", "content_type": 16}}, {"pk": 47, "model": "auth.permission", "fields": {"codename": "change_servercircuit", "name": "Can change server circuit", "content_type": 16}}, {"pk": 48, "model": "auth.permission", "fields": {"codename": "delete_servercircuit", "name": "Can delete server circuit", "content_type": 16}}, {"pk": 10, "model": "auth.permission", "fields": {"codename": "add_contenttype", "name": "Can add content type", "content_type": 4}}, {"pk": 11, "model": "auth.permission", "fields": {"codename": "change_contenttype", "name": "Can change content type", "content_type": 4}}, {"pk": 12, "model": "auth.permission", "fields": {"codename": "delete_contenttype", "name": "Can delete content type", "content_type": 4}}, {"pk": 82, "model": "auth.permission", "fields": {"codename": "add_offlinecomputedgrade", "name": "Can add offline computed grade", "content_type": 28}}, {"pk": 83, "model": "auth.permission", "fields": {"codename": "change_offlinecomputedgrade", "name": "Can change offline computed grade", "content_type": 28}}, {"pk": 84, "model": "auth.permission", "fields": {"codename": "delete_offlinecomputedgrade", "name": "Can delete offline computed grade", "content_type": 28}}, {"pk": 85, "model": "auth.permission", "fields": {"codename": "add_offlinecomputedgradelog", "name": "Can add offline computed grade log", "content_type": 29}}, {"pk": 86, "model": "auth.permission", "fields": {"codename": "change_offlinecomputedgradelog", "name": "Can change offline computed grade log", "content_type": 29}}, {"pk": 87, "model": "auth.permission", "fields": {"codename": "delete_offlinecomputedgradelog", "name": "Can delete offline computed grade log", "content_type": 29}}, {"pk": 67, "model": "auth.permission", "fields": {"codename": "add_studentmodule", "name": "Can add student module", "content_type": 23}}, {"pk": 68, "model": "auth.permission", "fields": {"codename": "change_studentmodule", "name": "Can change student module", "content_type": 23}}, {"pk": 69, "model": "auth.permission", "fields": {"codename": "delete_studentmodule", "name": "Can delete student module", "content_type": 23}}, {"pk": 70, "model": "auth.permission", "fields": {"codename": "add_studentmodulehistory", "name": "Can add student module history", "content_type": 24}}, {"pk": 71, "model": "auth.permission", "fields": {"codename": "change_studentmodulehistory", "name": "Can change student module history", "content_type": 24}}, {"pk": 72, "model": "auth.permission", "fields": {"codename": "delete_studentmodulehistory", "name": "Can delete student module history", "content_type": 24}}, {"pk": 79, "model": "auth.permission", "fields": {"codename": "add_xmodulestudentinfofield", "name": "Can add x module student info field", "content_type": 27}}, {"pk": 80, "model": "auth.permission", "fields": {"codename": "change_xmodulestudentinfofield", "name": "Can change x module student info field", "content_type": 27}}, {"pk": 81, "model": "auth.permission", "fields": {"codename": "delete_xmodulestudentinfofield", "name": "Can delete x module student info field", "content_type": 27}}, {"pk": 76, "model": "auth.permission", "fields": {"codename": "add_xmodulestudentprefsfield", "name": "Can add x module student prefs field", "content_type": 26}}, {"pk": 77, "model": "auth.permission", "fields": {"codename": "change_xmodulestudentprefsfield", "name": "Can change x module student prefs field", "content_type": 26}}, {"pk": 78, "model": "auth.permission", "fields": {"codename": "delete_xmodulestudentprefsfield", "name": "Can delete x module student prefs field", "content_type": 26}}, {"pk": 73, "model": "auth.permission", "fields": {"codename": "add_xmoduleuserstatesummaryfield", "name": "Can add x module user state summary field", "content_type": 25}}, {"pk": 74, "model": "auth.permission", "fields": {"codename": "change_xmoduleuserstatesummaryfield", "name": "Can change x module user state summary field", "content_type": 25}}, {"pk": 75, "model": "auth.permission", "fields": {"codename": "delete_xmoduleuserstatesummaryfield", "name": "Can delete x module user state summary field", "content_type": 25}}, {"pk": 52, "model": "auth.permission", "fields": {"codename": "add_courseusergroup", "name": "Can add course user group", "content_type": 18}}, {"pk": 53, "model": "auth.permission", "fields": {"codename": "change_courseusergroup", "name": "Can change course user group", "content_type": 18}}, {"pk": 54, "model": "auth.permission", "fields": {"codename": "delete_courseusergroup", "name": "Can delete course user group", "content_type": 18}}, {"pk": 214, "model": "auth.permission", "fields": {"codename": "add_coursemode", "name": "Can add course mode", "content_type": 71}}, {"pk": 215, "model": "auth.permission", "fields": {"codename": "change_coursemode", "name": "Can change course mode", "content_type": 71}}, {"pk": 216, "model": "auth.permission", "fields": {"codename": "delete_coursemode", "name": "Can delete course mode", "content_type": 71}}, {"pk": 190, "model": "auth.permission", "fields": {"codename": "add_notification", "name": "Can add notification", "content_type": 63}}, {"pk": 191, "model": "auth.permission", "fields": {"codename": "change_notification", "name": "Can change notification", "content_type": 63}}, {"pk": 192, "model": "auth.permission", "fields": {"codename": "delete_notification", "name": "Can delete notification", "content_type": 63}}, {"pk": 181, "model": "auth.permission", "fields": {"codename": "add_notificationtype", "name": "Can add type", "content_type": 60}}, {"pk": 182, "model": "auth.permission", "fields": {"codename": "change_notificationtype", "name": "Can change type", "content_type": 60}}, {"pk": 183, "model": "auth.permission", "fields": {"codename": "delete_notificationtype", "name": "Can delete type", "content_type": 60}}, {"pk": 184, "model": "auth.permission", "fields": {"codename": "add_settings", "name": "Can add settings", "content_type": 61}}, {"pk": 185, "model": "auth.permission", "fields": {"codename": "change_settings", "name": "Can change settings", "content_type": 61}}, {"pk": 186, "model": "auth.permission", "fields": {"codename": "delete_settings", "name": "Can delete settings", "content_type": 61}}, {"pk": 187, "model": "auth.permission", "fields": {"codename": "add_subscription", "name": "Can add subscription", "content_type": 62}}, {"pk": 188, "model": "auth.permission", "fields": {"codename": "change_subscription", "name": "Can change subscription", "content_type": 62}}, {"pk": 189, "model": "auth.permission", "fields": {"codename": "delete_subscription", "name": "Can delete subscription", "content_type": 62}}, {"pk": 58, "model": "auth.permission", "fields": {"codename": "add_association", "name": "Can add association", "content_type": 20}}, {"pk": 59, "model": "auth.permission", "fields": {"codename": "change_association", "name": "Can change association", "content_type": 20}}, {"pk": 60, "model": "auth.permission", "fields": {"codename": "delete_association", "name": "Can delete association", "content_type": 20}}, {"pk": 55, "model": "auth.permission", "fields": {"codename": "add_nonce", "name": "Can add nonce", "content_type": 19}}, {"pk": 56, "model": "auth.permission", "fields": {"codename": "change_nonce", "name": "Can change nonce", "content_type": 19}}, {"pk": 57, "model": "auth.permission", "fields": {"codename": "delete_nonce", "name": "Can delete nonce", "content_type": 19}}, {"pk": 61, "model": "auth.permission", "fields": {"codename": "add_useropenid", "name": "Can add user open id", "content_type": 21}}, {"pk": 62, "model": "auth.permission", "fields": {"codename": "change_useropenid", "name": "Can change user open id", "content_type": 21}}, {"pk": 63, "model": "auth.permission", "fields": {"codename": "delete_useropenid", "name": "Can delete user open id", "content_type": 21}}, {"pk": 28, "model": "auth.permission", "fields": {"codename": "add_crontabschedule", "name": "Can add crontab", "content_type": 10}}, {"pk": 29, "model": "auth.permission", "fields": {"codename": "change_crontabschedule", "name": "Can change crontab", "content_type": 10}}, {"pk": 30, "model": "auth.permission", "fields": {"codename": "delete_crontabschedule", "name": "Can delete crontab", "content_type": 10}}, {"pk": 25, "model": "auth.permission", "fields": {"codename": "add_intervalschedule", "name": "Can add interval", "content_type": 9}}, {"pk": 26, "model": "auth.permission", "fields": {"codename": "change_intervalschedule", "name": "Can change interval", "content_type": 9}}, {"pk": 27, "model": "auth.permission", "fields": {"codename": "delete_intervalschedule", "name": "Can delete interval", "content_type": 9}}, {"pk": 34, "model": "auth.permission", "fields": {"codename": "add_periodictask", "name": "Can add periodic task", "content_type": 12}}, {"pk": 35, "model": "auth.permission", "fields": {"codename": "change_periodictask", "name": "Can change periodic task", "content_type": 12}}, {"pk": 36, "model": "auth.permission", "fields": {"codename": "delete_periodictask", "name": "Can delete periodic task", "content_type": 12}}, {"pk": 31, "model": "auth.permission", "fields": {"codename": "add_periodictasks", "name": "Can add periodic tasks", "content_type": 11}}, {"pk": 32, "model": "auth.permission", "fields": {"codename": "change_periodictasks", "name": "Can change periodic tasks", "content_type": 11}}, {"pk": 33, "model": "auth.permission", "fields": {"codename": "delete_periodictasks", "name": "Can delete periodic tasks", "content_type": 11}}, {"pk": 19, "model": "auth.permission", "fields": {"codename": "add_taskmeta", "name": "Can add task state", "content_type": 7}}, {"pk": 20, "model": "auth.permission", "fields": {"codename": "change_taskmeta", "name": "Can change task state", "content_type": 7}}, {"pk": 21, "model": "auth.permission", "fields": {"codename": "delete_taskmeta", "name": "Can delete task state", "content_type": 7}}, {"pk": 22, "model": "auth.permission", "fields": {"codename": "add_tasksetmeta", "name": "Can add saved group result", "content_type": 8}}, {"pk": 23, "model": "auth.permission", "fields": {"codename": "change_tasksetmeta", "name": "Can change saved group result", "content_type": 8}}, {"pk": 24, "model": "auth.permission", "fields": {"codename": "delete_tasksetmeta", "name": "Can delete saved group result", "content_type": 8}}, {"pk": 40, "model": "auth.permission", "fields": {"codename": "add_taskstate", "name": "Can add task", "content_type": 14}}, {"pk": 41, "model": "auth.permission", "fields": {"codename": "change_taskstate", "name": "Can change task", "content_type": 14}}, {"pk": 42, "model": "auth.permission", "fields": {"codename": "delete_taskstate", "name": "Can delete task", "content_type": 14}}, {"pk": 37, "model": "auth.permission", "fields": {"codename": "add_workerstate", "name": "Can add worker", "content_type": 13}}, {"pk": 38, "model": "auth.permission", "fields": {"codename": "change_workerstate", "name": "Can change worker", "content_type": 13}}, {"pk": 39, "model": "auth.permission", "fields": {"codename": "delete_workerstate", "name": "Can delete worker", "content_type": 13}}, {"pk": 145, "model": "auth.permission", "fields": {"codename": "add_externalauthmap", "name": "Can add external auth map", "content_type": 49}}, {"pk": 146, "model": "auth.permission", "fields": {"codename": "change_externalauthmap", "name": "Can change external auth map", "content_type": 49}}, {"pk": 147, "model": "auth.permission", "fields": {"codename": "delete_externalauthmap", "name": "Can delete external auth map", "content_type": 49}}, {"pk": 196, "model": "auth.permission", "fields": {"codename": "add_puzzlecomplete", "name": "Can add puzzle complete", "content_type": 65}}, {"pk": 197, "model": "auth.permission", "fields": {"codename": "change_puzzlecomplete", "name": "Can change puzzle complete", "content_type": 65}}, {"pk": 198, "model": "auth.permission", "fields": {"codename": "delete_puzzlecomplete", "name": "Can delete puzzle complete", "content_type": 65}}, {"pk": 193, "model": "auth.permission", "fields": {"codename": "add_score", "name": "Can add score", "content_type": 64}}, {"pk": 194, "model": "auth.permission", "fields": {"codename": "change_score", "name": "Can change score", "content_type": 64}}, {"pk": 195, "model": "auth.permission", "fields": {"codename": "delete_score", "name": "Can delete score", "content_type": 64}}, {"pk": 124, "model": "auth.permission", "fields": {"codename": "add_instructortask", "name": "Can add instructor task", "content_type": 42}}, {"pk": 125, "model": "auth.permission", "fields": {"codename": "change_instructortask", "name": "Can change instructor task", "content_type": 42}}, {"pk": 126, "model": "auth.permission", "fields": {"codename": "delete_instructortask", "name": "Can delete instructor task", "content_type": 42}}, {"pk": 127, "model": "auth.permission", "fields": {"codename": "add_coursesoftware", "name": "Can add course software", "content_type": 43}}, {"pk": 128, "model": "auth.permission", "fields": {"codename": "change_coursesoftware", "name": "Can change course software", "content_type": 43}}, {"pk": 129, "model": "auth.permission", "fields": {"codename": "delete_coursesoftware", "name": "Can delete course software", "content_type": 43}}, {"pk": 130, "model": "auth.permission", "fields": {"codename": "add_userlicense", "name": "Can add user license", "content_type": 44}}, {"pk": 131, "model": "auth.permission", "fields": {"codename": "change_userlicense", "name": "Can change user license", "content_type": 44}}, {"pk": 132, "model": "auth.permission", "fields": {"codename": "delete_userlicense", "name": "Can delete user license", "content_type": 44}}, {"pk": 220, "model": "auth.permission", "fields": {"codename": "add_linkedin", "name": "Can add linked in", "content_type": 73}}, {"pk": 221, "model": "auth.permission", "fields": {"codename": "change_linkedin", "name": "Can change linked in", "content_type": 73}}, {"pk": 222, "model": "auth.permission", "fields": {"codename": "delete_linkedin", "name": "Can delete linked in", "content_type": 73}}, {"pk": 208, "model": "auth.permission", "fields": {"codename": "add_note", "name": "Can add note", "content_type": 69}}, {"pk": 209, "model": "auth.permission", "fields": {"codename": "change_note", "name": "Can change note", "content_type": 69}}, {"pk": 210, "model": "auth.permission", "fields": {"codename": "delete_note", "name": "Can delete note", "content_type": 69}}, {"pk": 49, "model": "auth.permission", "fields": {"codename": "add_psychometricdata", "name": "Can add psychometric data", "content_type": 17}}, {"pk": 50, "model": "auth.permission", "fields": {"codename": "change_psychometricdata", "name": "Can change psychometric data", "content_type": 17}}, {"pk": 51, "model": "auth.permission", "fields": {"codename": "delete_psychometricdata", "name": "Can delete psychometric data", "content_type": 17}}, {"pk": 13, "model": "auth.permission", "fields": {"codename": "add_session", "name": "Can add session", "content_type": 5}}, {"pk": 14, "model": "auth.permission", "fields": {"codename": "change_session", "name": "Can change session", "content_type": 5}}, {"pk": 15, "model": "auth.permission", "fields": {"codename": "delete_session", "name": "Can delete session", "content_type": 5}}, {"pk": 235, "model": "auth.permission", "fields": {"codename": "add_certificateitem", "name": "Can add certificate item", "content_type": 78}}, {"pk": 236, "model": "auth.permission", "fields": {"codename": "change_certificateitem", "name": "Can change certificate item", "content_type": 78}}, {"pk": 237, "model": "auth.permission", "fields": {"codename": "delete_certificateitem", "name": "Can delete certificate item", "content_type": 78}}, {"pk": 223, "model": "auth.permission", "fields": {"codename": "add_order", "name": "Can add order", "content_type": 74}}, {"pk": 224, "model": "auth.permission", "fields": {"codename": "change_order", "name": "Can change order", "content_type": 74}}, {"pk": 225, "model": "auth.permission", "fields": {"codename": "delete_order", "name": "Can delete order", "content_type": 74}}, {"pk": 226, "model": "auth.permission", "fields": {"codename": "add_orderitem", "name": "Can add order item", "content_type": 75}}, {"pk": 227, "model": "auth.permission", "fields": {"codename": "change_orderitem", "name": "Can change order item", "content_type": 75}}, {"pk": 228, "model": "auth.permission", "fields": {"codename": "delete_orderitem", "name": "Can delete order item", "content_type": 75}}, {"pk": 229, "model": "auth.permission", "fields": {"codename": "add_paidcourseregistration", "name": "Can add paid course registration", "content_type": 76}}, {"pk": 230, "model": "auth.permission", "fields": {"codename": "change_paidcourseregistration", "name": "Can change paid course registration", "content_type": 76}}, {"pk": 231, "model": "auth.permission", "fields": {"codename": "delete_paidcourseregistration", "name": "Can delete paid course registration", "content_type": 76}}, {"pk": 232, "model": "auth.permission", "fields": {"codename": "add_paidcourseregistrationannotation", "name": "Can add paid course registration annotation", "content_type": 77}}, {"pk": 233, "model": "auth.permission", "fields": {"codename": "change_paidcourseregistrationannotation", "name": "Can change paid course registration annotation", "content_type": 77}}, {"pk": 234, "model": "auth.permission", "fields": {"codename": "delete_paidcourseregistrationannotation", "name": "Can delete paid course registration annotation", "content_type": 77}}, {"pk": 16, "model": "auth.permission", "fields": {"codename": "add_site", "name": "Can add site", "content_type": 6}}, {"pk": 17, "model": "auth.permission", "fields": {"codename": "change_site", "name": "Can change site", "content_type": 6}}, {"pk": 18, "model": "auth.permission", "fields": {"codename": "delete_site", "name": "Can delete site", "content_type": 6}}, {"pk": 43, "model": "auth.permission", "fields": {"codename": "add_migrationhistory", "name": "Can add migration history", "content_type": 15}}, {"pk": 44, "model": "auth.permission", "fields": {"codename": "change_migrationhistory", "name": "Can change migration history", "content_type": 15}}, {"pk": 45, "model": "auth.permission", "fields": {"codename": "delete_migrationhistory", "name": "Can delete migration history", "content_type": 15}}, {"pk": 88, "model": "auth.permission", "fields": {"codename": "add_anonymoususerid", "name": "Can add anonymous user id", "content_type": 30}}, {"pk": 89, "model": "auth.permission", "fields": {"codename": "change_anonymoususerid", "name": "Can change anonymous user id", "content_type": 30}}, {"pk": 90, "model": "auth.permission", "fields": {"codename": "delete_anonymoususerid", "name": "Can delete anonymous user id", "content_type": 30}}, {"pk": 109, "model": "auth.permission", "fields": {"codename": "add_courseenrollment", "name": "Can add course enrollment", "content_type": 37}}, {"pk": 110, "model": "auth.permission", "fields": {"codename": "change_courseenrollment", "name": "Can change course enrollment", "content_type": 37}}, {"pk": 111, "model": "auth.permission", "fields": {"codename": "delete_courseenrollment", "name": "Can delete course enrollment", "content_type": 37}}, {"pk": 112, "model": "auth.permission", "fields": {"codename": "add_courseenrollmentallowed", "name": "Can add course enrollment allowed", "content_type": 38}}, {"pk": 113, "model": "auth.permission", "fields": {"codename": "change_courseenrollmentallowed", "name": "Can change course enrollment allowed", "content_type": 38}}, {"pk": 114, "model": "auth.permission", "fields": {"codename": "delete_courseenrollmentallowed", "name": "Can delete course enrollment allowed", "content_type": 38}}, {"pk": 106, "model": "auth.permission", "fields": {"codename": "add_pendingemailchange", "name": "Can add pending email change", "content_type": 36}}, {"pk": 107, "model": "auth.permission", "fields": {"codename": "change_pendingemailchange", "name": "Can change pending email change", "content_type": 36}}, {"pk": 108, "model": "auth.permission", "fields": {"codename": "delete_pendingemailchange", "name": "Can delete pending email change", "content_type": 36}}, {"pk": 103, "model": "auth.permission", "fields": {"codename": "add_pendingnamechange", "name": "Can add pending name change", "content_type": 35}}, {"pk": 104, "model": "auth.permission", "fields": {"codename": "change_pendingnamechange", "name": "Can change pending name change", "content_type": 35}}, {"pk": 105, "model": "auth.permission", "fields": {"codename": "delete_pendingnamechange", "name": "Can delete pending name change", "content_type": 35}}, {"pk": 100, "model": "auth.permission", "fields": {"codename": "add_registration", "name": "Can add registration", "content_type": 34}}, {"pk": 101, "model": "auth.permission", "fields": {"codename": "change_registration", "name": "Can change registration", "content_type": 34}}, {"pk": 102, "model": "auth.permission", "fields": {"codename": "delete_registration", "name": "Can delete registration", "content_type": 34}}, {"pk": 94, "model": "auth.permission", "fields": {"codename": "add_userprofile", "name": "Can add user profile", "content_type": 32}}, {"pk": 95, "model": "auth.permission", "fields": {"codename": "change_userprofile", "name": "Can change user profile", "content_type": 32}}, {"pk": 96, "model": "auth.permission", "fields": {"codename": "delete_userprofile", "name": "Can delete user profile", "content_type": 32}}, {"pk": 91, "model": "auth.permission", "fields": {"codename": "add_userstanding", "name": "Can add user standing", "content_type": 31}}, {"pk": 92, "model": "auth.permission", "fields": {"codename": "change_userstanding", "name": "Can change user standing", "content_type": 31}}, {"pk": 93, "model": "auth.permission", "fields": {"codename": "delete_userstanding", "name": "Can delete user standing", "content_type": 31}}, {"pk": 97, "model": "auth.permission", "fields": {"codename": "add_usertestgroup", "name": "Can add user test group", "content_type": 33}}, {"pk": 98, "model": "auth.permission", "fields": {"codename": "change_usertestgroup", "name": "Can change user test group", "content_type": 33}}, {"pk": 99, "model": "auth.permission", "fields": {"codename": "delete_usertestgroup", "name": "Can delete user test group", "content_type": 33}}, {"pk": 115, "model": "auth.permission", "fields": {"codename": "add_trackinglog", "name": "Can add tracking log", "content_type": 39}}, {"pk": 116, "model": "auth.permission", "fields": {"codename": "change_trackinglog", "name": "Can change tracking log", "content_type": 39}}, {"pk": 117, "model": "auth.permission", "fields": {"codename": "delete_trackinglog", "name": "Can delete tracking log", "content_type": 39}}, {"pk": 211, "model": "auth.permission", "fields": {"codename": "add_userpreference", "name": "Can add user preference", "content_type": 70}}, {"pk": 212, "model": "auth.permission", "fields": {"codename": "change_userpreference", "name": "Can change user preference", "content_type": 70}}, {"pk": 213, "model": "auth.permission", "fields": {"codename": "delete_userpreference", "name": "Can delete user preference", "content_type": 70}}, {"pk": 217, "model": "auth.permission", "fields": {"codename": "add_softwaresecurephotoverification", "name": "Can add software secure photo verification", "content_type": 72}}, {"pk": 218, "model": "auth.permission", "fields": {"codename": "change_softwaresecurephotoverification", "name": "Can change software secure photo verification", "content_type": 72}}, {"pk": 219, "model": "auth.permission", "fields": {"codename": "delete_softwaresecurephotoverification", "name": "Can delete software secure photo verification", "content_type": 72}}, {"pk": 199, "model": "auth.permission", "fields": {"codename": "add_flag", "name": "Can add flag", "content_type": 66}}, {"pk": 200, "model": "auth.permission", "fields": {"codename": "change_flag", "name": "Can change flag", "content_type": 66}}, {"pk": 201, "model": "auth.permission", "fields": {"codename": "delete_flag", "name": "Can delete flag", "content_type": 66}}, {"pk": 205, "model": "auth.permission", "fields": {"codename": "add_sample", "name": "Can add sample", "content_type": 68}}, {"pk": 206, "model": "auth.permission", "fields": {"codename": "change_sample", "name": "Can change sample", "content_type": 68}}, {"pk": 207, "model": "auth.permission", "fields": {"codename": "delete_sample", "name": "Can delete sample", "content_type": 68}}, {"pk": 202, "model": "auth.permission", "fields": {"codename": "add_switch", "name": "Can add switch", "content_type": 67}}, {"pk": 203, "model": "auth.permission", "fields": {"codename": "change_switch", "name": "Can change switch", "content_type": 67}}, {"pk": 204, "model": "auth.permission", "fields": {"codename": "delete_switch", "name": "Can delete switch", "content_type": 67}}, {"pk": 148, "model": "auth.permission", "fields": {"codename": "add_article", "name": "Can add article", "content_type": 50}}, {"pk": 152, "model": "auth.permission", "fields": {"codename": "assign", "name": "Can change ownership of any article", "content_type": 50}}, {"pk": 149, "model": "auth.permission", "fields": {"codename": "change_article", "name": "Can change article", "content_type": 50}}, {"pk": 150, "model": "auth.permission", "fields": {"codename": "delete_article", "name": "Can delete article", "content_type": 50}}, {"pk": 153, "model": "auth.permission", "fields": {"codename": "grant", "name": "Can assign permissions to other users", "content_type": 50}}, {"pk": 151, "model": "auth.permission", "fields": {"codename": "moderate", "name": "Can edit all articles and lock/unlock/restore", "content_type": 50}}, {"pk": 154, "model": "auth.permission", "fields": {"codename": "add_articleforobject", "name": "Can add Article for object", "content_type": 51}}, {"pk": 155, "model": "auth.permission", "fields": {"codename": "change_articleforobject", "name": "Can change Article for object", "content_type": 51}}, {"pk": 156, "model": "auth.permission", "fields": {"codename": "delete_articleforobject", "name": "Can delete Article for object", "content_type": 51}}, {"pk": 163, "model": "auth.permission", "fields": {"codename": "add_articleplugin", "name": "Can add article plugin", "content_type": 54}}, {"pk": 164, "model": "auth.permission", "fields": {"codename": "change_articleplugin", "name": "Can change article plugin", "content_type": 54}}, {"pk": 165, "model": "auth.permission", "fields": {"codename": "delete_articleplugin", "name": "Can delete article plugin", "content_type": 54}}, {"pk": 157, "model": "auth.permission", "fields": {"codename": "add_articlerevision", "name": "Can add article revision", "content_type": 52}}, {"pk": 158, "model": "auth.permission", "fields": {"codename": "change_articlerevision", "name": "Can change article revision", "content_type": 52}}, {"pk": 159, "model": "auth.permission", "fields": {"codename": "delete_articlerevision", "name": "Can delete article revision", "content_type": 52}}, {"pk": 178, "model": "auth.permission", "fields": {"codename": "add_articlesubscription", "name": "Can add article subscription", "content_type": 59}}, {"pk": 179, "model": "auth.permission", "fields": {"codename": "change_articlesubscription", "name": "Can change article subscription", "content_type": 59}}, {"pk": 180, "model": "auth.permission", "fields": {"codename": "delete_articlesubscription", "name": "Can delete article subscription", "content_type": 59}}, {"pk": 166, "model": "auth.permission", "fields": {"codename": "add_reusableplugin", "name": "Can add reusable plugin", "content_type": 55}}, {"pk": 167, "model": "auth.permission", "fields": {"codename": "change_reusableplugin", "name": "Can change reusable plugin", "content_type": 55}}, {"pk": 168, "model": "auth.permission", "fields": {"codename": "delete_reusableplugin", "name": "Can delete reusable plugin", "content_type": 55}}, {"pk": 172, "model": "auth.permission", "fields": {"codename": "add_revisionplugin", "name": "Can add revision plugin", "content_type": 57}}, {"pk": 173, "model": "auth.permission", "fields": {"codename": "change_revisionplugin", "name": "Can change revision plugin", "content_type": 57}}, {"pk": 174, "model": "auth.permission", "fields": {"codename": "delete_revisionplugin", "name": "Can delete revision plugin", "content_type": 57}}, {"pk": 175, "model": "auth.permission", "fields": {"codename": "add_revisionpluginrevision", "name": "Can add revision plugin revision", "content_type": 58}}, {"pk": 176, "model": "auth.permission", "fields": {"codename": "change_revisionpluginrevision", "name": "Can change revision plugin revision", "content_type": 58}}, {"pk": 177, "model": "auth.permission", "fields": {"codename": "delete_revisionpluginrevision", "name": "Can delete revision plugin revision", "content_type": 58}}, {"pk": 169, "model": "auth.permission", "fields": {"codename": "add_simpleplugin", "name": "Can add simple plugin", "content_type": 56}}, {"pk": 170, "model": "auth.permission", "fields": {"codename": "change_simpleplugin", "name": "Can change simple plugin", "content_type": 56}}, {"pk": 171, "model": "auth.permission", "fields": {"codename": "delete_simpleplugin", "name": "Can delete simple plugin", "content_type": 56}}, {"pk": 160, "model": "auth.permission", "fields": {"codename": "add_urlpath", "name": "Can add URL path", "content_type": 53}}, {"pk": 161, "model": "auth.permission", "fields": {"codename": "change_urlpath", "name": "Can change URL path", "content_type": 53}}, {"pk": 162, "model": "auth.permission", "fields": {"codename": "delete_urlpath", "name": "Can delete URL path", "content_type": 53}}] \ No newline at end of file +[{"pk": 30, "model": "contenttypes.contenttype", "fields": {"model": "anonymoususerid", "name": "anonymous user id", "app_label": "student"}}, {"pk": 51, "model": "contenttypes.contenttype", "fields": {"model": "article", "name": "article", "app_label": "wiki"}}, {"pk": 52, "model": "contenttypes.contenttype", "fields": {"model": "articleforobject", "name": "Article for object", "app_label": "wiki"}}, {"pk": 55, "model": "contenttypes.contenttype", "fields": {"model": "articleplugin", "name": "article plugin", "app_label": "wiki"}}, {"pk": 53, "model": "contenttypes.contenttype", "fields": {"model": "articlerevision", "name": "article revision", "app_label": "wiki"}}, {"pk": 60, "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": 85, "model": "contenttypes.contenttype", "fields": {"model": "certificateitem", "name": "certificate item", "app_label": "shoppingcart"}}, {"pk": 41, "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": 49, "model": "contenttypes.contenttype", "fields": {"model": "courseauthorization", "name": "course authorization", "app_label": "bulk_email"}}, {"pk": 46, "model": "contenttypes.contenttype", "fields": {"model": "courseemail", "name": "course email", "app_label": "bulk_email"}}, {"pk": 48, "model": "contenttypes.contenttype", "fields": {"model": "courseemailtemplate", "name": "course email template", "app_label": "bulk_email"}}, {"pk": 38, "model": "contenttypes.contenttype", "fields": {"model": "courseenrollment", "name": "course enrollment", "app_label": "student"}}, {"pk": 39, "model": "contenttypes.contenttype", "fields": {"model": "courseenrollmentallowed", "name": "course enrollment allowed", "app_label": "student"}}, {"pk": 73, "model": "contenttypes.contenttype", "fields": {"model": "coursemode", "name": "course mode", "app_label": "course_modes"}}, {"pk": 44, "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": 75, "model": "contenttypes.contenttype", "fields": {"model": "darklangconfig", "name": "dark lang config", "app_label": "dark_lang"}}, {"pk": 77, "model": "contenttypes.contenttype", "fields": {"model": "embargoedcourse", "name": "embargoed course", "app_label": "embargo"}}, {"pk": 78, "model": "contenttypes.contenttype", "fields": {"model": "embargoedstate", "name": "embargoed state", "app_label": "embargo"}}, {"pk": 50, "model": "contenttypes.contenttype", "fields": {"model": "externalauthmap", "name": "external auth map", "app_label": "external_auth"}}, {"pk": 67, "model": "contenttypes.contenttype", "fields": {"model": "flag", "name": "flag", "app_label": "waffle"}}, {"pk": 42, "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": 43, "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": 79, "model": "contenttypes.contenttype", "fields": {"model": "ipfilter", "name": "ip filter", "app_label": "embargo"}}, {"pk": 80, "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": 37, "model": "contenttypes.contenttype", "fields": {"model": "loginfailures", "name": "login failures", "app_label": "student"}}, {"pk": 76, "model": "contenttypes.contenttype", "fields": {"model": "midcoursereverificationwindow", "name": "midcourse reverification window", "app_label": "reverification"}}, {"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": 70, "model": "contenttypes.contenttype", "fields": {"model": "note", "name": "note", "app_label": "notes"}}, {"pk": 64, "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": 47, "model": "contenttypes.contenttype", "fields": {"model": "optout", "name": "optout", "app_label": "bulk_email"}}, {"pk": 81, "model": "contenttypes.contenttype", "fields": {"model": "order", "name": "order", "app_label": "shoppingcart"}}, {"pk": 82, "model": "contenttypes.contenttype", "fields": {"model": "orderitem", "name": "order item", "app_label": "shoppingcart"}}, {"pk": 83, "model": "contenttypes.contenttype", "fields": {"model": "paidcourseregistration", "name": "paid course registration", "app_label": "shoppingcart"}}, {"pk": 84, "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": 66, "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": 56, "model": "contenttypes.contenttype", "fields": {"model": "reusableplugin", "name": "reusable plugin", "app_label": "wiki"}}, {"pk": 58, "model": "contenttypes.contenttype", "fields": {"model": "revisionplugin", "name": "revision plugin", "app_label": "wiki"}}, {"pk": 59, "model": "contenttypes.contenttype", "fields": {"model": "revisionpluginrevision", "name": "revision plugin revision", "app_label": "wiki"}}, {"pk": 69, "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": 65, "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": 62, "model": "contenttypes.contenttype", "fields": {"model": "settings", "name": "settings", "app_label": "django_notify"}}, {"pk": 57, "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": 74, "model": "contenttypes.contenttype", "fields": {"model": "softwaresecurephotoverification", "name": "software secure photo verification", "app_label": "verify_student"}}, {"pk": 71, "model": "contenttypes.contenttype", "fields": {"model": "splashconfig", "name": "splash config", "app_label": "splash"}}, {"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": 63, "model": "contenttypes.contenttype", "fields": {"model": "subscription", "name": "subscription", "app_label": "django_notify"}}, {"pk": 68, "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": 40, "model": "contenttypes.contenttype", "fields": {"model": "trackinglog", "name": "tracking log", "app_label": "track"}}, {"pk": 61, "model": "contenttypes.contenttype", "fields": {"model": "notificationtype", "name": "type", "app_label": "django_notify"}}, {"pk": 54, "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": 45, "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": 72, "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-02-26T16:03:03Z", "app_name": "courseware", "migration": "0001_initial"}}, {"pk": 2, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:03Z", "app_name": "courseware", "migration": "0002_add_indexes"}}, {"pk": 3, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:03Z", "app_name": "courseware", "migration": "0003_done_grade_cache"}}, {"pk": 4, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:03Z", "app_name": "courseware", "migration": "0004_add_field_studentmodule_course_id"}}, {"pk": 5, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:03Z", "app_name": "courseware", "migration": "0005_auto__add_offlinecomputedgrade__add_unique_offlinecomputedgrade_user_c"}}, {"pk": 6, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:03Z", "app_name": "courseware", "migration": "0006_create_student_module_history"}}, {"pk": 7, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:03Z", "app_name": "courseware", "migration": "0007_allow_null_version_in_history"}}, {"pk": 8, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:04Z", "app_name": "courseware", "migration": "0008_add_xmodule_storage"}}, {"pk": 9, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:04Z", "app_name": "courseware", "migration": "0009_add_field_default"}}, {"pk": 10, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:04Z", "app_name": "courseware", "migration": "0010_rename_xblock_field_content_to_user_state_summary"}}, {"pk": 11, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:04Z", "app_name": "student", "migration": "0001_initial"}}, {"pk": 12, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:04Z", "app_name": "student", "migration": "0002_text_to_varchar_and_indexes"}}, {"pk": 13, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:04Z", "app_name": "student", "migration": "0003_auto__add_usertestgroup"}}, {"pk": 14, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:04Z", "app_name": "student", "migration": "0004_add_email_index"}}, {"pk": 15, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0005_name_change"}}, {"pk": 16, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0006_expand_meta_field"}}, {"pk": 17, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0007_convert_to_utf8"}}, {"pk": 18, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0008__auto__add_courseregistration"}}, {"pk": 19, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0009_auto__del_courseregistration__add_courseenrollment"}}, {"pk": 20, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0010_auto__chg_field_courseenrollment_course_id"}}, {"pk": 21, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0011_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use"}}, {"pk": 22, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0012_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt"}}, {"pk": 23, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0013_auto__chg_field_userprofile_meta"}}, {"pk": 24, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0014_auto__del_courseenrollment"}}, {"pk": 25, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0015_auto__add_courseenrollment__add_unique_courseenrollment_user_course_id"}}, {"pk": 26, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0016_auto__add_field_courseenrollment_date__chg_field_userprofile_country"}}, {"pk": 27, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0017_rename_date_to_created"}}, {"pk": 28, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0018_auto"}}, {"pk": 29, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:05Z", "app_name": "student", "migration": "0019_create_approved_demographic_fields_fall_2012"}}, {"pk": 30, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:06Z", "app_name": "student", "migration": "0020_add_test_center_user"}}, {"pk": 31, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:06Z", "app_name": "student", "migration": "0021_remove_askbot"}}, {"pk": 32, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:06Z", "app_name": "student", "migration": "0022_auto__add_courseenrollmentallowed__add_unique_courseenrollmentallowed_"}}, {"pk": 33, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:06Z", "app_name": "student", "migration": "0023_add_test_center_registration"}}, {"pk": 34, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:06Z", "app_name": "student", "migration": "0024_add_allow_certificate"}}, {"pk": 35, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:06Z", "app_name": "student", "migration": "0025_auto__add_field_courseenrollmentallowed_auto_enroll"}}, {"pk": 36, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:06Z", "app_name": "student", "migration": "0026_auto__remove_index_student_testcenterregistration_accommodation_request"}}, {"pk": 37, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:06Z", "app_name": "student", "migration": "0027_add_active_flag_and_mode_to_courseware_enrollment"}}, {"pk": 38, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:06Z", "app_name": "student", "migration": "0028_auto__add_userstanding"}}, {"pk": 39, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:06Z", "app_name": "student", "migration": "0029_add_lookup_table_between_user_and_anonymous_student_id"}}, {"pk": 40, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "student", "migration": "0029_remove_pearson"}}, {"pk": 41, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "student", "migration": "0030_auto__chg_field_anonymoususerid_anonymous_user_id"}}, {"pk": 42, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "student", "migration": "0031_drop_student_anonymoususerid_temp_archive"}}, {"pk": 43, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "student", "migration": "0032_add_field_UserProfile_country_add_field_UserProfile_city"}}, {"pk": 44, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "student", "migration": "0032_auto__add_loginfailures"}}, {"pk": 45, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "track", "migration": "0001_initial"}}, {"pk": 46, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "track", "migration": "0002_auto__add_field_trackinglog_host__chg_field_trackinglog_event_type__ch"}}, {"pk": 47, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "certificates", "migration": "0001_added_generatedcertificates"}}, {"pk": 48, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "certificates", "migration": "0002_auto__add_field_generatedcertificate_download_url"}}, {"pk": 49, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "certificates", "migration": "0003_auto__add_field_generatedcertificate_enabled"}}, {"pk": 50, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "certificates", "migration": "0004_auto__add_field_generatedcertificate_graded_certificate_id__add_field_"}}, {"pk": 51, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "certificates", "migration": "0005_auto__add_field_generatedcertificate_name"}}, {"pk": 52, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "certificates", "migration": "0006_auto__chg_field_generatedcertificate_certificate_id"}}, {"pk": 53, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:07Z", "app_name": "certificates", "migration": "0007_auto__add_revokedcertificate"}}, {"pk": 54, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:08Z", "app_name": "certificates", "migration": "0008_auto__del_revokedcertificate__del_field_generatedcertificate_name__add"}}, {"pk": 55, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:08Z", "app_name": "certificates", "migration": "0009_auto__del_field_generatedcertificate_graded_download_url__del_field_ge"}}, {"pk": 56, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:08Z", "app_name": "certificates", "migration": "0010_auto__del_field_generatedcertificate_enabled__add_field_generatedcerti"}}, {"pk": 57, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:08Z", "app_name": "certificates", "migration": "0011_auto__del_field_generatedcertificate_certificate_id__add_field_generat"}}, {"pk": 58, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:08Z", "app_name": "certificates", "migration": "0012_auto__add_field_generatedcertificate_name__add_field_generatedcertific"}}, {"pk": 59, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:08Z", "app_name": "certificates", "migration": "0013_auto__add_field_generatedcertificate_error_reason"}}, {"pk": 60, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:08Z", "app_name": "certificates", "migration": "0014_adding_whitelist"}}, {"pk": 61, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:08Z", "app_name": "certificates", "migration": "0015_adding_mode_for_verified_certs"}}, {"pk": 62, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:08Z", "app_name": "instructor_task", "migration": "0001_initial"}}, {"pk": 63, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:08Z", "app_name": "instructor_task", "migration": "0002_add_subtask_field"}}, {"pk": 64, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:08Z", "app_name": "licenses", "migration": "0001_initial"}}, {"pk": 65, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:09Z", "app_name": "bulk_email", "migration": "0001_initial"}}, {"pk": 66, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:09Z", "app_name": "bulk_email", "migration": "0002_change_field_names"}}, {"pk": 67, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:09Z", "app_name": "bulk_email", "migration": "0003_add_optout_user"}}, {"pk": 68, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:09Z", "app_name": "bulk_email", "migration": "0004_migrate_optout_user"}}, {"pk": 69, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:09Z", "app_name": "bulk_email", "migration": "0005_remove_optout_email"}}, {"pk": 70, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:09Z", "app_name": "bulk_email", "migration": "0006_add_course_email_template"}}, {"pk": 71, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:09Z", "app_name": "bulk_email", "migration": "0007_load_course_email_template"}}, {"pk": 72, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:09Z", "app_name": "bulk_email", "migration": "0008_add_course_authorizations"}}, {"pk": 73, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:09Z", "app_name": "bulk_email", "migration": "0009_force_unique_course_ids"}}, {"pk": 74, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:09Z", "app_name": "external_auth", "migration": "0001_initial"}}, {"pk": 75, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:10Z", "app_name": "wiki", "migration": "0001_initial"}}, {"pk": 76, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:10Z", "app_name": "wiki", "migration": "0002_auto__add_field_articleplugin_created"}}, {"pk": 77, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:10Z", "app_name": "wiki", "migration": "0003_auto__add_field_urlpath_article"}}, {"pk": 78, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:10Z", "app_name": "wiki", "migration": "0004_populate_urlpath__article"}}, {"pk": 79, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:10Z", "app_name": "wiki", "migration": "0005_auto__chg_field_urlpath_article"}}, {"pk": 80, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:11Z", "app_name": "wiki", "migration": "0006_auto__add_attachmentrevision__add_image__add_attachment"}}, {"pk": 81, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:11Z", "app_name": "wiki", "migration": "0007_auto__add_articlesubscription"}}, {"pk": 82, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:11Z", "app_name": "wiki", "migration": "0008_auto__add_simpleplugin__add_revisionpluginrevision__add_imagerevision_"}}, {"pk": 83, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:11Z", "app_name": "wiki", "migration": "0009_auto__add_field_imagerevision_width__add_field_imagerevision_height"}}, {"pk": 84, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:11Z", "app_name": "wiki", "migration": "0010_auto__chg_field_imagerevision_image"}}, {"pk": 85, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:11Z", "app_name": "wiki", "migration": "0011_auto__chg_field_imagerevision_width__chg_field_imagerevision_height"}}, {"pk": 86, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:12Z", "app_name": "django_notify", "migration": "0001_initial"}}, {"pk": 87, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:12Z", "app_name": "notifications", "migration": "0001_initial"}}, {"pk": 88, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:12Z", "app_name": "foldit", "migration": "0001_initial"}}, {"pk": 89, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:13Z", "app_name": "waffle", "migration": "0001_initial"}}, {"pk": 90, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:13Z", "app_name": "waffle", "migration": "0002_auto__add_sample"}}, {"pk": 91, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:13Z", "app_name": "waffle", "migration": "0003_auto__add_field_flag_note__add_field_switch_note__add_field_sample_not"}}, {"pk": 92, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:13Z", "app_name": "waffle", "migration": "0004_auto__add_field_flag_testing"}}, {"pk": 93, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:13Z", "app_name": "waffle", "migration": "0005_auto__add_field_flag_created__add_field_flag_modified"}}, {"pk": 94, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:13Z", "app_name": "waffle", "migration": "0006_auto__add_field_switch_created__add_field_switch_modified__add_field_s"}}, {"pk": 95, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:13Z", "app_name": "waffle", "migration": "0007_auto__chg_field_flag_created__chg_field_flag_modified__chg_field_switc"}}, {"pk": 96, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:13Z", "app_name": "waffle", "migration": "0008_auto__add_field_flag_languages"}}, {"pk": 97, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:13Z", "app_name": "django_comment_client", "migration": "0001_initial"}}, {"pk": 98, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:13Z", "app_name": "django_comment_common", "migration": "0001_initial"}}, {"pk": 99, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:14Z", "app_name": "notes", "migration": "0001_initial"}}, {"pk": 100, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:14Z", "app_name": "splash", "migration": "0001_initial"}}, {"pk": 101, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:14Z", "app_name": "splash", "migration": "0002_auto__add_field_splashconfig_unaffected_url_paths"}}, {"pk": 102, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:14Z", "app_name": "user_api", "migration": "0001_initial"}}, {"pk": 103, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:14Z", "app_name": "course_modes", "migration": "0001_initial"}}, {"pk": 104, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:14Z", "app_name": "course_modes", "migration": "0002_auto__add_field_coursemode_currency"}}, {"pk": 105, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:15Z", "app_name": "course_modes", "migration": "0003_auto__add_unique_coursemode_course_id_currency_mode_slug"}}, {"pk": 106, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:15Z", "app_name": "course_modes", "migration": "0004_auto__add_field_coursemode_expiration_date"}}, {"pk": 107, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:15Z", "app_name": "course_modes", "migration": "0005_auto__add_field_coursemode_expiration_datetime"}}, {"pk": 108, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:15Z", "app_name": "course_modes", "migration": "0006_expiration_date_to_datetime"}}, {"pk": 109, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:15Z", "app_name": "verify_student", "migration": "0001_initial"}}, {"pk": 110, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:15Z", "app_name": "verify_student", "migration": "0002_auto__add_field_softwaresecurephotoverification_window"}}, {"pk": 111, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:15Z", "app_name": "verify_student", "migration": "0003_auto__add_field_softwaresecurephotoverification_display"}}, {"pk": 112, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:15Z", "app_name": "dark_lang", "migration": "0001_initial"}}, {"pk": 113, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:15Z", "app_name": "dark_lang", "migration": "0002_enable_on_install"}}, {"pk": 114, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:16Z", "app_name": "reverification", "migration": "0001_initial"}}, {"pk": 115, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:16Z", "app_name": "embargo", "migration": "0001_initial"}}, {"pk": 116, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:16Z", "app_name": "linkedin", "migration": "0001_initial"}}, {"pk": 117, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:16Z", "app_name": "django_extensions", "migration": "0001_empty"}}, {"pk": 118, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:16Z", "app_name": "shoppingcart", "migration": "0001_initial"}}, {"pk": 119, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:17Z", "app_name": "shoppingcart", "migration": "0002_auto__add_field_paidcourseregistration_mode"}}, {"pk": 120, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:17Z", "app_name": "shoppingcart", "migration": "0003_auto__del_field_orderitem_line_cost"}}, {"pk": 121, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:17Z", "app_name": "shoppingcart", "migration": "0004_auto__add_field_orderitem_fulfilled_time"}}, {"pk": 122, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:17Z", "app_name": "shoppingcart", "migration": "0005_auto__add_paidcourseregistrationannotation__add_field_orderitem_report"}}, {"pk": 123, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:17Z", "app_name": "shoppingcart", "migration": "0006_auto__add_field_order_refunded_time__add_field_orderitem_refund_reques"}}, {"pk": 124, "model": "south.migrationhistory", "fields": {"applied": "2014-02-26T16:03:17Z", "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}

edX
Connect with edX:        

{course_title}


{{message_body}}
       
Copyright \u00a9 2013 edX, All rights reserved.


Our mailing address is:
edX
11 Cambridge Center, Suite 101
Cambridge, MA, USA 02142


This email was automatically sent from {platform_name}.
You are receiving this email at address {email} because you are enrolled in {course_title}.
To stop receiving email like this, update your course email settings here.
"}}, {"pk": 64, "model": "auth.permission", "fields": {"codename": "add_logentry", "name": "Can add log entry", "content_type": 22}}, {"pk": 65, "model": "auth.permission", "fields": {"codename": "change_logentry", "name": "Can change log entry", "content_type": 22}}, {"pk": 66, "model": "auth.permission", "fields": {"codename": "delete_logentry", "name": "Can delete log entry", "content_type": 22}}, {"pk": 4, "model": "auth.permission", "fields": {"codename": "add_group", "name": "Can add group", "content_type": 2}}, {"pk": 5, "model": "auth.permission", "fields": {"codename": "change_group", "name": "Can change group", "content_type": 2}}, {"pk": 6, "model": "auth.permission", "fields": {"codename": "delete_group", "name": "Can delete group", "content_type": 2}}, {"pk": 1, "model": "auth.permission", "fields": {"codename": "add_permission", "name": "Can add permission", "content_type": 1}}, {"pk": 2, "model": "auth.permission", "fields": {"codename": "change_permission", "name": "Can change permission", "content_type": 1}}, {"pk": 3, "model": "auth.permission", "fields": {"codename": "delete_permission", "name": "Can delete permission", "content_type": 1}}, {"pk": 7, "model": "auth.permission", "fields": {"codename": "add_user", "name": "Can add user", "content_type": 3}}, {"pk": 8, "model": "auth.permission", "fields": {"codename": "change_user", "name": "Can change user", "content_type": 3}}, {"pk": 9, "model": "auth.permission", "fields": {"codename": "delete_user", "name": "Can delete user", "content_type": 3}}, {"pk": 145, "model": "auth.permission", "fields": {"codename": "add_courseauthorization", "name": "Can add course authorization", "content_type": 49}}, {"pk": 146, "model": "auth.permission", "fields": {"codename": "change_courseauthorization", "name": "Can change course authorization", "content_type": 49}}, {"pk": 147, "model": "auth.permission", "fields": {"codename": "delete_courseauthorization", "name": "Can delete course authorization", "content_type": 49}}, {"pk": 136, "model": "auth.permission", "fields": {"codename": "add_courseemail", "name": "Can add course email", "content_type": 46}}, {"pk": 137, "model": "auth.permission", "fields": {"codename": "change_courseemail", "name": "Can change course email", "content_type": 46}}, {"pk": 138, "model": "auth.permission", "fields": {"codename": "delete_courseemail", "name": "Can delete course email", "content_type": 46}}, {"pk": 142, "model": "auth.permission", "fields": {"codename": "add_courseemailtemplate", "name": "Can add course email template", "content_type": 48}}, {"pk": 143, "model": "auth.permission", "fields": {"codename": "change_courseemailtemplate", "name": "Can change course email template", "content_type": 48}}, {"pk": 144, "model": "auth.permission", "fields": {"codename": "delete_courseemailtemplate", "name": "Can delete course email template", "content_type": 48}}, {"pk": 139, "model": "auth.permission", "fields": {"codename": "add_optout", "name": "Can add optout", "content_type": 47}}, {"pk": 140, "model": "auth.permission", "fields": {"codename": "change_optout", "name": "Can change optout", "content_type": 47}}, {"pk": 141, "model": "auth.permission", "fields": {"codename": "delete_optout", "name": "Can delete optout", "content_type": 47}}, {"pk": 121, "model": "auth.permission", "fields": {"codename": "add_certificatewhitelist", "name": "Can add certificate whitelist", "content_type": 41}}, {"pk": 122, "model": "auth.permission", "fields": {"codename": "change_certificatewhitelist", "name": "Can change certificate whitelist", "content_type": 41}}, {"pk": 123, "model": "auth.permission", "fields": {"codename": "delete_certificatewhitelist", "name": "Can delete certificate whitelist", "content_type": 41}}, {"pk": 124, "model": "auth.permission", "fields": {"codename": "add_generatedcertificate", "name": "Can add generated certificate", "content_type": 42}}, {"pk": 125, "model": "auth.permission", "fields": {"codename": "change_generatedcertificate", "name": "Can change generated certificate", "content_type": 42}}, {"pk": 126, "model": "auth.permission", "fields": {"codename": "delete_generatedcertificate", "name": "Can delete generated certificate", "content_type": 42}}, {"pk": 46, "model": "auth.permission", "fields": {"codename": "add_servercircuit", "name": "Can add server circuit", "content_type": 16}}, {"pk": 47, "model": "auth.permission", "fields": {"codename": "change_servercircuit", "name": "Can change server circuit", "content_type": 16}}, {"pk": 48, "model": "auth.permission", "fields": {"codename": "delete_servercircuit", "name": "Can delete server circuit", "content_type": 16}}, {"pk": 10, "model": "auth.permission", "fields": {"codename": "add_contenttype", "name": "Can add content type", "content_type": 4}}, {"pk": 11, "model": "auth.permission", "fields": {"codename": "change_contenttype", "name": "Can change content type", "content_type": 4}}, {"pk": 12, "model": "auth.permission", "fields": {"codename": "delete_contenttype", "name": "Can delete content type", "content_type": 4}}, {"pk": 82, "model": "auth.permission", "fields": {"codename": "add_offlinecomputedgrade", "name": "Can add offline computed grade", "content_type": 28}}, {"pk": 83, "model": "auth.permission", "fields": {"codename": "change_offlinecomputedgrade", "name": "Can change offline computed grade", "content_type": 28}}, {"pk": 84, "model": "auth.permission", "fields": {"codename": "delete_offlinecomputedgrade", "name": "Can delete offline computed grade", "content_type": 28}}, {"pk": 85, "model": "auth.permission", "fields": {"codename": "add_offlinecomputedgradelog", "name": "Can add offline computed grade log", "content_type": 29}}, {"pk": 86, "model": "auth.permission", "fields": {"codename": "change_offlinecomputedgradelog", "name": "Can change offline computed grade log", "content_type": 29}}, {"pk": 87, "model": "auth.permission", "fields": {"codename": "delete_offlinecomputedgradelog", "name": "Can delete offline computed grade log", "content_type": 29}}, {"pk": 67, "model": "auth.permission", "fields": {"codename": "add_studentmodule", "name": "Can add student module", "content_type": 23}}, {"pk": 68, "model": "auth.permission", "fields": {"codename": "change_studentmodule", "name": "Can change student module", "content_type": 23}}, {"pk": 69, "model": "auth.permission", "fields": {"codename": "delete_studentmodule", "name": "Can delete student module", "content_type": 23}}, {"pk": 70, "model": "auth.permission", "fields": {"codename": "add_studentmodulehistory", "name": "Can add student module history", "content_type": 24}}, {"pk": 71, "model": "auth.permission", "fields": {"codename": "change_studentmodulehistory", "name": "Can change student module history", "content_type": 24}}, {"pk": 72, "model": "auth.permission", "fields": {"codename": "delete_studentmodulehistory", "name": "Can delete student module history", "content_type": 24}}, {"pk": 79, "model": "auth.permission", "fields": {"codename": "add_xmodulestudentinfofield", "name": "Can add x module student info field", "content_type": 27}}, {"pk": 80, "model": "auth.permission", "fields": {"codename": "change_xmodulestudentinfofield", "name": "Can change x module student info field", "content_type": 27}}, {"pk": 81, "model": "auth.permission", "fields": {"codename": "delete_xmodulestudentinfofield", "name": "Can delete x module student info field", "content_type": 27}}, {"pk": 76, "model": "auth.permission", "fields": {"codename": "add_xmodulestudentprefsfield", "name": "Can add x module student prefs field", "content_type": 26}}, {"pk": 77, "model": "auth.permission", "fields": {"codename": "change_xmodulestudentprefsfield", "name": "Can change x module student prefs field", "content_type": 26}}, {"pk": 78, "model": "auth.permission", "fields": {"codename": "delete_xmodulestudentprefsfield", "name": "Can delete x module student prefs field", "content_type": 26}}, {"pk": 73, "model": "auth.permission", "fields": {"codename": "add_xmoduleuserstatesummaryfield", "name": "Can add x module user state summary field", "content_type": 25}}, {"pk": 74, "model": "auth.permission", "fields": {"codename": "change_xmoduleuserstatesummaryfield", "name": "Can change x module user state summary field", "content_type": 25}}, {"pk": 75, "model": "auth.permission", "fields": {"codename": "delete_xmoduleuserstatesummaryfield", "name": "Can delete x module user state summary field", "content_type": 25}}, {"pk": 52, "model": "auth.permission", "fields": {"codename": "add_courseusergroup", "name": "Can add course user group", "content_type": 18}}, {"pk": 53, "model": "auth.permission", "fields": {"codename": "change_courseusergroup", "name": "Can change course user group", "content_type": 18}}, {"pk": 54, "model": "auth.permission", "fields": {"codename": "delete_courseusergroup", "name": "Can delete course user group", "content_type": 18}}, {"pk": 220, "model": "auth.permission", "fields": {"codename": "add_coursemode", "name": "Can add course mode", "content_type": 73}}, {"pk": 221, "model": "auth.permission", "fields": {"codename": "change_coursemode", "name": "Can change course mode", "content_type": 73}}, {"pk": 222, "model": "auth.permission", "fields": {"codename": "delete_coursemode", "name": "Can delete course mode", "content_type": 73}}, {"pk": 226, "model": "auth.permission", "fields": {"codename": "add_darklangconfig", "name": "Can add dark lang config", "content_type": 75}}, {"pk": 227, "model": "auth.permission", "fields": {"codename": "change_darklangconfig", "name": "Can change dark lang config", "content_type": 75}}, {"pk": 228, "model": "auth.permission", "fields": {"codename": "delete_darklangconfig", "name": "Can delete dark lang config", "content_type": 75}}, {"pk": 193, "model": "auth.permission", "fields": {"codename": "add_notification", "name": "Can add notification", "content_type": 64}}, {"pk": 194, "model": "auth.permission", "fields": {"codename": "change_notification", "name": "Can change notification", "content_type": 64}}, {"pk": 195, "model": "auth.permission", "fields": {"codename": "delete_notification", "name": "Can delete notification", "content_type": 64}}, {"pk": 184, "model": "auth.permission", "fields": {"codename": "add_notificationtype", "name": "Can add type", "content_type": 61}}, {"pk": 185, "model": "auth.permission", "fields": {"codename": "change_notificationtype", "name": "Can change type", "content_type": 61}}, {"pk": 186, "model": "auth.permission", "fields": {"codename": "delete_notificationtype", "name": "Can delete type", "content_type": 61}}, {"pk": 187, "model": "auth.permission", "fields": {"codename": "add_settings", "name": "Can add settings", "content_type": 62}}, {"pk": 188, "model": "auth.permission", "fields": {"codename": "change_settings", "name": "Can change settings", "content_type": 62}}, {"pk": 189, "model": "auth.permission", "fields": {"codename": "delete_settings", "name": "Can delete settings", "content_type": 62}}, {"pk": 190, "model": "auth.permission", "fields": {"codename": "add_subscription", "name": "Can add subscription", "content_type": 63}}, {"pk": 191, "model": "auth.permission", "fields": {"codename": "change_subscription", "name": "Can change subscription", "content_type": 63}}, {"pk": 192, "model": "auth.permission", "fields": {"codename": "delete_subscription", "name": "Can delete subscription", "content_type": 63}}, {"pk": 58, "model": "auth.permission", "fields": {"codename": "add_association", "name": "Can add association", "content_type": 20}}, {"pk": 59, "model": "auth.permission", "fields": {"codename": "change_association", "name": "Can change association", "content_type": 20}}, {"pk": 60, "model": "auth.permission", "fields": {"codename": "delete_association", "name": "Can delete association", "content_type": 20}}, {"pk": 55, "model": "auth.permission", "fields": {"codename": "add_nonce", "name": "Can add nonce", "content_type": 19}}, {"pk": 56, "model": "auth.permission", "fields": {"codename": "change_nonce", "name": "Can change nonce", "content_type": 19}}, {"pk": 57, "model": "auth.permission", "fields": {"codename": "delete_nonce", "name": "Can delete nonce", "content_type": 19}}, {"pk": 61, "model": "auth.permission", "fields": {"codename": "add_useropenid", "name": "Can add user open id", "content_type": 21}}, {"pk": 62, "model": "auth.permission", "fields": {"codename": "change_useropenid", "name": "Can change user open id", "content_type": 21}}, {"pk": 63, "model": "auth.permission", "fields": {"codename": "delete_useropenid", "name": "Can delete user open id", "content_type": 21}}, {"pk": 28, "model": "auth.permission", "fields": {"codename": "add_crontabschedule", "name": "Can add crontab", "content_type": 10}}, {"pk": 29, "model": "auth.permission", "fields": {"codename": "change_crontabschedule", "name": "Can change crontab", "content_type": 10}}, {"pk": 30, "model": "auth.permission", "fields": {"codename": "delete_crontabschedule", "name": "Can delete crontab", "content_type": 10}}, {"pk": 25, "model": "auth.permission", "fields": {"codename": "add_intervalschedule", "name": "Can add interval", "content_type": 9}}, {"pk": 26, "model": "auth.permission", "fields": {"codename": "change_intervalschedule", "name": "Can change interval", "content_type": 9}}, {"pk": 27, "model": "auth.permission", "fields": {"codename": "delete_intervalschedule", "name": "Can delete interval", "content_type": 9}}, {"pk": 34, "model": "auth.permission", "fields": {"codename": "add_periodictask", "name": "Can add periodic task", "content_type": 12}}, {"pk": 35, "model": "auth.permission", "fields": {"codename": "change_periodictask", "name": "Can change periodic task", "content_type": 12}}, {"pk": 36, "model": "auth.permission", "fields": {"codename": "delete_periodictask", "name": "Can delete periodic task", "content_type": 12}}, {"pk": 31, "model": "auth.permission", "fields": {"codename": "add_periodictasks", "name": "Can add periodic tasks", "content_type": 11}}, {"pk": 32, "model": "auth.permission", "fields": {"codename": "change_periodictasks", "name": "Can change periodic tasks", "content_type": 11}}, {"pk": 33, "model": "auth.permission", "fields": {"codename": "delete_periodictasks", "name": "Can delete periodic tasks", "content_type": 11}}, {"pk": 19, "model": "auth.permission", "fields": {"codename": "add_taskmeta", "name": "Can add task state", "content_type": 7}}, {"pk": 20, "model": "auth.permission", "fields": {"codename": "change_taskmeta", "name": "Can change task state", "content_type": 7}}, {"pk": 21, "model": "auth.permission", "fields": {"codename": "delete_taskmeta", "name": "Can delete task state", "content_type": 7}}, {"pk": 22, "model": "auth.permission", "fields": {"codename": "add_tasksetmeta", "name": "Can add saved group result", "content_type": 8}}, {"pk": 23, "model": "auth.permission", "fields": {"codename": "change_tasksetmeta", "name": "Can change saved group result", "content_type": 8}}, {"pk": 24, "model": "auth.permission", "fields": {"codename": "delete_tasksetmeta", "name": "Can delete saved group result", "content_type": 8}}, {"pk": 40, "model": "auth.permission", "fields": {"codename": "add_taskstate", "name": "Can add task", "content_type": 14}}, {"pk": 41, "model": "auth.permission", "fields": {"codename": "change_taskstate", "name": "Can change task", "content_type": 14}}, {"pk": 42, "model": "auth.permission", "fields": {"codename": "delete_taskstate", "name": "Can delete task", "content_type": 14}}, {"pk": 37, "model": "auth.permission", "fields": {"codename": "add_workerstate", "name": "Can add worker", "content_type": 13}}, {"pk": 38, "model": "auth.permission", "fields": {"codename": "change_workerstate", "name": "Can change worker", "content_type": 13}}, {"pk": 39, "model": "auth.permission", "fields": {"codename": "delete_workerstate", "name": "Can delete worker", "content_type": 13}}, {"pk": 232, "model": "auth.permission", "fields": {"codename": "add_embargoedcourse", "name": "Can add embargoed course", "content_type": 77}}, {"pk": 233, "model": "auth.permission", "fields": {"codename": "change_embargoedcourse", "name": "Can change embargoed course", "content_type": 77}}, {"pk": 234, "model": "auth.permission", "fields": {"codename": "delete_embargoedcourse", "name": "Can delete embargoed course", "content_type": 77}}, {"pk": 235, "model": "auth.permission", "fields": {"codename": "add_embargoedstate", "name": "Can add embargoed state", "content_type": 78}}, {"pk": 236, "model": "auth.permission", "fields": {"codename": "change_embargoedstate", "name": "Can change embargoed state", "content_type": 78}}, {"pk": 237, "model": "auth.permission", "fields": {"codename": "delete_embargoedstate", "name": "Can delete embargoed state", "content_type": 78}}, {"pk": 238, "model": "auth.permission", "fields": {"codename": "add_ipfilter", "name": "Can add ip filter", "content_type": 79}}, {"pk": 239, "model": "auth.permission", "fields": {"codename": "change_ipfilter", "name": "Can change ip filter", "content_type": 79}}, {"pk": 240, "model": "auth.permission", "fields": {"codename": "delete_ipfilter", "name": "Can delete ip filter", "content_type": 79}}, {"pk": 148, "model": "auth.permission", "fields": {"codename": "add_externalauthmap", "name": "Can add external auth map", "content_type": 50}}, {"pk": 149, "model": "auth.permission", "fields": {"codename": "change_externalauthmap", "name": "Can change external auth map", "content_type": 50}}, {"pk": 150, "model": "auth.permission", "fields": {"codename": "delete_externalauthmap", "name": "Can delete external auth map", "content_type": 50}}, {"pk": 199, "model": "auth.permission", "fields": {"codename": "add_puzzlecomplete", "name": "Can add puzzle complete", "content_type": 66}}, {"pk": 200, "model": "auth.permission", "fields": {"codename": "change_puzzlecomplete", "name": "Can change puzzle complete", "content_type": 66}}, {"pk": 201, "model": "auth.permission", "fields": {"codename": "delete_puzzlecomplete", "name": "Can delete puzzle complete", "content_type": 66}}, {"pk": 196, "model": "auth.permission", "fields": {"codename": "add_score", "name": "Can add score", "content_type": 65}}, {"pk": 197, "model": "auth.permission", "fields": {"codename": "change_score", "name": "Can change score", "content_type": 65}}, {"pk": 198, "model": "auth.permission", "fields": {"codename": "delete_score", "name": "Can delete score", "content_type": 65}}, {"pk": 127, "model": "auth.permission", "fields": {"codename": "add_instructortask", "name": "Can add instructor task", "content_type": 43}}, {"pk": 128, "model": "auth.permission", "fields": {"codename": "change_instructortask", "name": "Can change instructor task", "content_type": 43}}, {"pk": 129, "model": "auth.permission", "fields": {"codename": "delete_instructortask", "name": "Can delete instructor task", "content_type": 43}}, {"pk": 130, "model": "auth.permission", "fields": {"codename": "add_coursesoftware", "name": "Can add course software", "content_type": 44}}, {"pk": 131, "model": "auth.permission", "fields": {"codename": "change_coursesoftware", "name": "Can change course software", "content_type": 44}}, {"pk": 132, "model": "auth.permission", "fields": {"codename": "delete_coursesoftware", "name": "Can delete course software", "content_type": 44}}, {"pk": 133, "model": "auth.permission", "fields": {"codename": "add_userlicense", "name": "Can add user license", "content_type": 45}}, {"pk": 134, "model": "auth.permission", "fields": {"codename": "change_userlicense", "name": "Can change user license", "content_type": 45}}, {"pk": 135, "model": "auth.permission", "fields": {"codename": "delete_userlicense", "name": "Can delete user license", "content_type": 45}}, {"pk": 241, "model": "auth.permission", "fields": {"codename": "add_linkedin", "name": "Can add linked in", "content_type": 80}}, {"pk": 242, "model": "auth.permission", "fields": {"codename": "change_linkedin", "name": "Can change linked in", "content_type": 80}}, {"pk": 243, "model": "auth.permission", "fields": {"codename": "delete_linkedin", "name": "Can delete linked in", "content_type": 80}}, {"pk": 211, "model": "auth.permission", "fields": {"codename": "add_note", "name": "Can add note", "content_type": 70}}, {"pk": 212, "model": "auth.permission", "fields": {"codename": "change_note", "name": "Can change note", "content_type": 70}}, {"pk": 213, "model": "auth.permission", "fields": {"codename": "delete_note", "name": "Can delete note", "content_type": 70}}, {"pk": 49, "model": "auth.permission", "fields": {"codename": "add_psychometricdata", "name": "Can add psychometric data", "content_type": 17}}, {"pk": 50, "model": "auth.permission", "fields": {"codename": "change_psychometricdata", "name": "Can change psychometric data", "content_type": 17}}, {"pk": 51, "model": "auth.permission", "fields": {"codename": "delete_psychometricdata", "name": "Can delete psychometric data", "content_type": 17}}, {"pk": 229, "model": "auth.permission", "fields": {"codename": "add_midcoursereverificationwindow", "name": "Can add midcourse reverification window", "content_type": 76}}, {"pk": 230, "model": "auth.permission", "fields": {"codename": "change_midcoursereverificationwindow", "name": "Can change midcourse reverification window", "content_type": 76}}, {"pk": 231, "model": "auth.permission", "fields": {"codename": "delete_midcoursereverificationwindow", "name": "Can delete midcourse reverification window", "content_type": 76}}, {"pk": 13, "model": "auth.permission", "fields": {"codename": "add_session", "name": "Can add session", "content_type": 5}}, {"pk": 14, "model": "auth.permission", "fields": {"codename": "change_session", "name": "Can change session", "content_type": 5}}, {"pk": 15, "model": "auth.permission", "fields": {"codename": "delete_session", "name": "Can delete session", "content_type": 5}}, {"pk": 256, "model": "auth.permission", "fields": {"codename": "add_certificateitem", "name": "Can add certificate item", "content_type": 85}}, {"pk": 257, "model": "auth.permission", "fields": {"codename": "change_certificateitem", "name": "Can change certificate item", "content_type": 85}}, {"pk": 258, "model": "auth.permission", "fields": {"codename": "delete_certificateitem", "name": "Can delete certificate item", "content_type": 85}}, {"pk": 244, "model": "auth.permission", "fields": {"codename": "add_order", "name": "Can add order", "content_type": 81}}, {"pk": 245, "model": "auth.permission", "fields": {"codename": "change_order", "name": "Can change order", "content_type": 81}}, {"pk": 246, "model": "auth.permission", "fields": {"codename": "delete_order", "name": "Can delete order", "content_type": 81}}, {"pk": 247, "model": "auth.permission", "fields": {"codename": "add_orderitem", "name": "Can add order item", "content_type": 82}}, {"pk": 248, "model": "auth.permission", "fields": {"codename": "change_orderitem", "name": "Can change order item", "content_type": 82}}, {"pk": 249, "model": "auth.permission", "fields": {"codename": "delete_orderitem", "name": "Can delete order item", "content_type": 82}}, {"pk": 250, "model": "auth.permission", "fields": {"codename": "add_paidcourseregistration", "name": "Can add paid course registration", "content_type": 83}}, {"pk": 251, "model": "auth.permission", "fields": {"codename": "change_paidcourseregistration", "name": "Can change paid course registration", "content_type": 83}}, {"pk": 252, "model": "auth.permission", "fields": {"codename": "delete_paidcourseregistration", "name": "Can delete paid course registration", "content_type": 83}}, {"pk": 253, "model": "auth.permission", "fields": {"codename": "add_paidcourseregistrationannotation", "name": "Can add paid course registration annotation", "content_type": 84}}, {"pk": 254, "model": "auth.permission", "fields": {"codename": "change_paidcourseregistrationannotation", "name": "Can change paid course registration annotation", "content_type": 84}}, {"pk": 255, "model": "auth.permission", "fields": {"codename": "delete_paidcourseregistrationannotation", "name": "Can delete paid course registration annotation", "content_type": 84}}, {"pk": 16, "model": "auth.permission", "fields": {"codename": "add_site", "name": "Can add site", "content_type": 6}}, {"pk": 17, "model": "auth.permission", "fields": {"codename": "change_site", "name": "Can change site", "content_type": 6}}, {"pk": 18, "model": "auth.permission", "fields": {"codename": "delete_site", "name": "Can delete site", "content_type": 6}}, {"pk": 43, "model": "auth.permission", "fields": {"codename": "add_migrationhistory", "name": "Can add migration history", "content_type": 15}}, {"pk": 44, "model": "auth.permission", "fields": {"codename": "change_migrationhistory", "name": "Can change migration history", "content_type": 15}}, {"pk": 45, "model": "auth.permission", "fields": {"codename": "delete_migrationhistory", "name": "Can delete migration history", "content_type": 15}}, {"pk": 214, "model": "auth.permission", "fields": {"codename": "add_splashconfig", "name": "Can add splash config", "content_type": 71}}, {"pk": 215, "model": "auth.permission", "fields": {"codename": "change_splashconfig", "name": "Can change splash config", "content_type": 71}}, {"pk": 216, "model": "auth.permission", "fields": {"codename": "delete_splashconfig", "name": "Can delete splash config", "content_type": 71}}, {"pk": 88, "model": "auth.permission", "fields": {"codename": "add_anonymoususerid", "name": "Can add anonymous user id", "content_type": 30}}, {"pk": 89, "model": "auth.permission", "fields": {"codename": "change_anonymoususerid", "name": "Can change anonymous user id", "content_type": 30}}, {"pk": 90, "model": "auth.permission", "fields": {"codename": "delete_anonymoususerid", "name": "Can delete anonymous user id", "content_type": 30}}, {"pk": 112, "model": "auth.permission", "fields": {"codename": "add_courseenrollment", "name": "Can add course enrollment", "content_type": 38}}, {"pk": 113, "model": "auth.permission", "fields": {"codename": "change_courseenrollment", "name": "Can change course enrollment", "content_type": 38}}, {"pk": 114, "model": "auth.permission", "fields": {"codename": "delete_courseenrollment", "name": "Can delete course enrollment", "content_type": 38}}, {"pk": 115, "model": "auth.permission", "fields": {"codename": "add_courseenrollmentallowed", "name": "Can add course enrollment allowed", "content_type": 39}}, {"pk": 116, "model": "auth.permission", "fields": {"codename": "change_courseenrollmentallowed", "name": "Can change course enrollment allowed", "content_type": 39}}, {"pk": 117, "model": "auth.permission", "fields": {"codename": "delete_courseenrollmentallowed", "name": "Can delete course enrollment allowed", "content_type": 39}}, {"pk": 109, "model": "auth.permission", "fields": {"codename": "add_loginfailures", "name": "Can add login failures", "content_type": 37}}, {"pk": 110, "model": "auth.permission", "fields": {"codename": "change_loginfailures", "name": "Can change login failures", "content_type": 37}}, {"pk": 111, "model": "auth.permission", "fields": {"codename": "delete_loginfailures", "name": "Can delete login failures", "content_type": 37}}, {"pk": 106, "model": "auth.permission", "fields": {"codename": "add_pendingemailchange", "name": "Can add pending email change", "content_type": 36}}, {"pk": 107, "model": "auth.permission", "fields": {"codename": "change_pendingemailchange", "name": "Can change pending email change", "content_type": 36}}, {"pk": 108, "model": "auth.permission", "fields": {"codename": "delete_pendingemailchange", "name": "Can delete pending email change", "content_type": 36}}, {"pk": 103, "model": "auth.permission", "fields": {"codename": "add_pendingnamechange", "name": "Can add pending name change", "content_type": 35}}, {"pk": 104, "model": "auth.permission", "fields": {"codename": "change_pendingnamechange", "name": "Can change pending name change", "content_type": 35}}, {"pk": 105, "model": "auth.permission", "fields": {"codename": "delete_pendingnamechange", "name": "Can delete pending name change", "content_type": 35}}, {"pk": 100, "model": "auth.permission", "fields": {"codename": "add_registration", "name": "Can add registration", "content_type": 34}}, {"pk": 101, "model": "auth.permission", "fields": {"codename": "change_registration", "name": "Can change registration", "content_type": 34}}, {"pk": 102, "model": "auth.permission", "fields": {"codename": "delete_registration", "name": "Can delete registration", "content_type": 34}}, {"pk": 94, "model": "auth.permission", "fields": {"codename": "add_userprofile", "name": "Can add user profile", "content_type": 32}}, {"pk": 95, "model": "auth.permission", "fields": {"codename": "change_userprofile", "name": "Can change user profile", "content_type": 32}}, {"pk": 96, "model": "auth.permission", "fields": {"codename": "delete_userprofile", "name": "Can delete user profile", "content_type": 32}}, {"pk": 91, "model": "auth.permission", "fields": {"codename": "add_userstanding", "name": "Can add user standing", "content_type": 31}}, {"pk": 92, "model": "auth.permission", "fields": {"codename": "change_userstanding", "name": "Can change user standing", "content_type": 31}}, {"pk": 93, "model": "auth.permission", "fields": {"codename": "delete_userstanding", "name": "Can delete user standing", "content_type": 31}}, {"pk": 97, "model": "auth.permission", "fields": {"codename": "add_usertestgroup", "name": "Can add user test group", "content_type": 33}}, {"pk": 98, "model": "auth.permission", "fields": {"codename": "change_usertestgroup", "name": "Can change user test group", "content_type": 33}}, {"pk": 99, "model": "auth.permission", "fields": {"codename": "delete_usertestgroup", "name": "Can delete user test group", "content_type": 33}}, {"pk": 118, "model": "auth.permission", "fields": {"codename": "add_trackinglog", "name": "Can add tracking log", "content_type": 40}}, {"pk": 119, "model": "auth.permission", "fields": {"codename": "change_trackinglog", "name": "Can change tracking log", "content_type": 40}}, {"pk": 120, "model": "auth.permission", "fields": {"codename": "delete_trackinglog", "name": "Can delete tracking log", "content_type": 40}}, {"pk": 217, "model": "auth.permission", "fields": {"codename": "add_userpreference", "name": "Can add user preference", "content_type": 72}}, {"pk": 218, "model": "auth.permission", "fields": {"codename": "change_userpreference", "name": "Can change user preference", "content_type": 72}}, {"pk": 219, "model": "auth.permission", "fields": {"codename": "delete_userpreference", "name": "Can delete user preference", "content_type": 72}}, {"pk": 223, "model": "auth.permission", "fields": {"codename": "add_softwaresecurephotoverification", "name": "Can add software secure photo verification", "content_type": 74}}, {"pk": 224, "model": "auth.permission", "fields": {"codename": "change_softwaresecurephotoverification", "name": "Can change software secure photo verification", "content_type": 74}}, {"pk": 225, "model": "auth.permission", "fields": {"codename": "delete_softwaresecurephotoverification", "name": "Can delete software secure photo verification", "content_type": 74}}, {"pk": 202, "model": "auth.permission", "fields": {"codename": "add_flag", "name": "Can add flag", "content_type": 67}}, {"pk": 203, "model": "auth.permission", "fields": {"codename": "change_flag", "name": "Can change flag", "content_type": 67}}, {"pk": 204, "model": "auth.permission", "fields": {"codename": "delete_flag", "name": "Can delete flag", "content_type": 67}}, {"pk": 208, "model": "auth.permission", "fields": {"codename": "add_sample", "name": "Can add sample", "content_type": 69}}, {"pk": 209, "model": "auth.permission", "fields": {"codename": "change_sample", "name": "Can change sample", "content_type": 69}}, {"pk": 210, "model": "auth.permission", "fields": {"codename": "delete_sample", "name": "Can delete sample", "content_type": 69}}, {"pk": 205, "model": "auth.permission", "fields": {"codename": "add_switch", "name": "Can add switch", "content_type": 68}}, {"pk": 206, "model": "auth.permission", "fields": {"codename": "change_switch", "name": "Can change switch", "content_type": 68}}, {"pk": 207, "model": "auth.permission", "fields": {"codename": "delete_switch", "name": "Can delete switch", "content_type": 68}}, {"pk": 151, "model": "auth.permission", "fields": {"codename": "add_article", "name": "Can add article", "content_type": 51}}, {"pk": 155, "model": "auth.permission", "fields": {"codename": "assign", "name": "Can change ownership of any article", "content_type": 51}}, {"pk": 152, "model": "auth.permission", "fields": {"codename": "change_article", "name": "Can change article", "content_type": 51}}, {"pk": 153, "model": "auth.permission", "fields": {"codename": "delete_article", "name": "Can delete article", "content_type": 51}}, {"pk": 156, "model": "auth.permission", "fields": {"codename": "grant", "name": "Can assign permissions to other users", "content_type": 51}}, {"pk": 154, "model": "auth.permission", "fields": {"codename": "moderate", "name": "Can edit all articles and lock/unlock/restore", "content_type": 51}}, {"pk": 157, "model": "auth.permission", "fields": {"codename": "add_articleforobject", "name": "Can add Article for object", "content_type": 52}}, {"pk": 158, "model": "auth.permission", "fields": {"codename": "change_articleforobject", "name": "Can change Article for object", "content_type": 52}}, {"pk": 159, "model": "auth.permission", "fields": {"codename": "delete_articleforobject", "name": "Can delete Article for object", "content_type": 52}}, {"pk": 166, "model": "auth.permission", "fields": {"codename": "add_articleplugin", "name": "Can add article plugin", "content_type": 55}}, {"pk": 167, "model": "auth.permission", "fields": {"codename": "change_articleplugin", "name": "Can change article plugin", "content_type": 55}}, {"pk": 168, "model": "auth.permission", "fields": {"codename": "delete_articleplugin", "name": "Can delete article plugin", "content_type": 55}}, {"pk": 160, "model": "auth.permission", "fields": {"codename": "add_articlerevision", "name": "Can add article revision", "content_type": 53}}, {"pk": 161, "model": "auth.permission", "fields": {"codename": "change_articlerevision", "name": "Can change article revision", "content_type": 53}}, {"pk": 162, "model": "auth.permission", "fields": {"codename": "delete_articlerevision", "name": "Can delete article revision", "content_type": 53}}, {"pk": 181, "model": "auth.permission", "fields": {"codename": "add_articlesubscription", "name": "Can add article subscription", "content_type": 60}}, {"pk": 182, "model": "auth.permission", "fields": {"codename": "change_articlesubscription", "name": "Can change article subscription", "content_type": 60}}, {"pk": 183, "model": "auth.permission", "fields": {"codename": "delete_articlesubscription", "name": "Can delete article subscription", "content_type": 60}}, {"pk": 169, "model": "auth.permission", "fields": {"codename": "add_reusableplugin", "name": "Can add reusable plugin", "content_type": 56}}, {"pk": 170, "model": "auth.permission", "fields": {"codename": "change_reusableplugin", "name": "Can change reusable plugin", "content_type": 56}}, {"pk": 171, "model": "auth.permission", "fields": {"codename": "delete_reusableplugin", "name": "Can delete reusable plugin", "content_type": 56}}, {"pk": 175, "model": "auth.permission", "fields": {"codename": "add_revisionplugin", "name": "Can add revision plugin", "content_type": 58}}, {"pk": 176, "model": "auth.permission", "fields": {"codename": "change_revisionplugin", "name": "Can change revision plugin", "content_type": 58}}, {"pk": 177, "model": "auth.permission", "fields": {"codename": "delete_revisionplugin", "name": "Can delete revision plugin", "content_type": 58}}, {"pk": 178, "model": "auth.permission", "fields": {"codename": "add_revisionpluginrevision", "name": "Can add revision plugin revision", "content_type": 59}}, {"pk": 179, "model": "auth.permission", "fields": {"codename": "change_revisionpluginrevision", "name": "Can change revision plugin revision", "content_type": 59}}, {"pk": 180, "model": "auth.permission", "fields": {"codename": "delete_revisionpluginrevision", "name": "Can delete revision plugin revision", "content_type": 59}}, {"pk": 172, "model": "auth.permission", "fields": {"codename": "add_simpleplugin", "name": "Can add simple plugin", "content_type": 57}}, {"pk": 173, "model": "auth.permission", "fields": {"codename": "change_simpleplugin", "name": "Can change simple plugin", "content_type": 57}}, {"pk": 174, "model": "auth.permission", "fields": {"codename": "delete_simpleplugin", "name": "Can delete simple plugin", "content_type": 57}}, {"pk": 163, "model": "auth.permission", "fields": {"codename": "add_urlpath", "name": "Can add URL path", "content_type": 54}}, {"pk": 164, "model": "auth.permission", "fields": {"codename": "change_urlpath", "name": "Can change URL path", "content_type": 54}}, {"pk": 165, "model": "auth.permission", "fields": {"codename": "delete_urlpath", "name": "Can delete URL path", "content_type": 54}}, {"pk": 1, "model": "dark_lang.darklangconfig", "fields": {"change_date": "2014-02-26T16:03:15Z", "changed_by": null, "enabled": true, "released_languages": ""}}] \ No newline at end of file diff --git a/common/test/db_cache/bok_choy_schema.sql b/common/test/db_cache/bok_choy_schema.sql index 276ebdf778..47ccd20578 100644 --- a/common/test/db_cache/bok_choy_schema.sql +++ b/common/test/db_cache/bok_choy_schema.sql @@ -46,7 +46,7 @@ CREATE TABLE `auth_permission` ( UNIQUE KEY `content_type_id` (`content_type_id`,`codename`), KEY `auth_permission_e4470c6e` (`content_type_id`), CONSTRAINT `content_type_id_refs_id_728de91f` FOREIGN KEY (`content_type_id`) REFERENCES `django_content_type` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=238 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=259 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `auth_registration`; /*!40101 SET @saved_cs_client = @@character_set_client */; @@ -122,12 +122,14 @@ CREATE TABLE `auth_userprofile` ( `location` varchar(255) NOT NULL, `meta` longtext NOT NULL, `courseware` varchar(255) NOT NULL, - `gender` varchar(6) DEFAULT NULL, + `gender` varchar(6), `mailing_address` longtext, - `year_of_birth` int(11) DEFAULT NULL, - `level_of_education` varchar(6) DEFAULT NULL, + `year_of_birth` int(11), + `level_of_education` varchar(6), `goals` longtext, `allow_certificate` tinyint(1) NOT NULL, + `country` varchar(2), + `city` longtext, PRIMARY KEY (`id`), UNIQUE KEY `user_id` (`user_id`), KEY `auth_userprofile_52094d6e` (`name`), @@ -147,6 +149,7 @@ CREATE TABLE `bulk_email_courseauthorization` ( `course_id` varchar(255) NOT NULL, `email_enabled` tinyint(1) NOT NULL, PRIMARY KEY (`id`), + UNIQUE KEY `bulk_email_courseauthorization_course_id_4f6cee675bf93275_uniq` (`course_id`), KEY `bulk_email_courseauthorization_ff48d8e5` (`course_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -187,7 +190,7 @@ DROP TABLE IF EXISTS `bulk_email_optout`; CREATE TABLE `bulk_email_optout` ( `id` int(11) NOT NULL AUTO_INCREMENT, `course_id` varchar(255) NOT NULL, - `user_id` int(11) DEFAULT NULL, + `user_id` int(11), PRIMARY KEY (`id`), UNIQUE KEY `bulk_email_optout_course_id_368f7519b2997e1a_uniq` (`course_id`,`user_id`), KEY `bulk_email_optout_ff48d8e5` (`course_id`), @@ -366,7 +369,7 @@ CREATE TABLE `courseware_studentmodule` ( `grade` double DEFAULT NULL, `created` datetime NOT NULL, `modified` datetime NOT NULL, - `max_grade` double DEFAULT NULL, + `max_grade` double, `done` varchar(8) NOT NULL, `course_id` varchar(255) NOT NULL, PRIMARY KEY (`id`), @@ -388,7 +391,7 @@ DROP TABLE IF EXISTS `courseware_studentmodulehistory`; CREATE TABLE `courseware_studentmodulehistory` ( `id` int(11) NOT NULL AUTO_INCREMENT, `student_module_id` int(11) NOT NULL, - `version` varchar(255) DEFAULT NULL, + `version` varchar(255), `created` datetime NOT NULL, `state` longtext, `grade` double DEFAULT NULL, @@ -458,6 +461,20 @@ CREATE TABLE `courseware_xmoduleuserstatesummaryfield` ( KEY `courseware_xmodulecontentfield_5436e97a` (`modified`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `dark_lang_darklangconfig`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `dark_lang_darklangconfig` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `change_date` datetime NOT NULL, + `changed_by_id` int(11) DEFAULT NULL, + `enabled` tinyint(1) NOT NULL, + `released_languages` longtext NOT NULL, + PRIMARY KEY (`id`), + KEY `dark_lang_darklangconfig_16905482` (`changed_by_id`), + CONSTRAINT `changed_by_id_refs_id_3fb19c355c5fe834` FOREIGN KEY (`changed_by_id`) REFERENCES `auth_user` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `django_admin_log`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; @@ -536,7 +553,7 @@ CREATE TABLE `django_content_type` ( `model` varchar(100) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `app_label` (`app_label`,`model`) -) ENGINE=InnoDB AUTO_INCREMENT=79 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=86 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `django_openid_auth_association`; /*!40101 SET @saved_cs_client = @@character_set_client */; @@ -644,8 +661,8 @@ CREATE TABLE `djcelery_periodictask` ( UNIQUE KEY `name` (`name`), KEY `djcelery_periodictask_17d2d99d` (`interval_id`), KEY `djcelery_periodictask_7aa5fda` (`crontab_id`), - CONSTRAINT `interval_id_refs_id_f2054349` FOREIGN KEY (`interval_id`) REFERENCES `djcelery_intervalschedule` (`id`), - CONSTRAINT `crontab_id_refs_id_ebff5e74` FOREIGN KEY (`crontab_id`) REFERENCES `djcelery_crontabschedule` (`id`) + CONSTRAINT `crontab_id_refs_id_ebff5e74` FOREIGN KEY (`crontab_id`) REFERENCES `djcelery_crontabschedule` (`id`), + CONSTRAINT `interval_id_refs_id_f2054349` FOREIGN KEY (`interval_id`) REFERENCES `djcelery_intervalschedule` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `djcelery_periodictasks`; @@ -698,6 +715,46 @@ CREATE TABLE `djcelery_workerstate` ( KEY `djcelery_workerstate_eb8ac7e4` (`last_heartbeat`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `embargo_embargoedcourse`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `embargo_embargoedcourse` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `course_id` varchar(255) NOT NULL, + `embargoed` tinyint(1) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `course_id` (`course_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `embargo_embargoedstate`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `embargo_embargoedstate` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `change_date` datetime NOT NULL, + `changed_by_id` int(11) DEFAULT NULL, + `enabled` tinyint(1) NOT NULL, + `embargoed_countries` longtext NOT NULL, + PRIMARY KEY (`id`), + KEY `embargo_embargoedstate_16905482` (`changed_by_id`), + CONSTRAINT `changed_by_id_refs_id_3c8b83add0205d39` FOREIGN KEY (`changed_by_id`) REFERENCES `auth_user` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `embargo_ipfilter`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `embargo_ipfilter` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `change_date` datetime NOT NULL, + `changed_by_id` int(11) DEFAULT NULL, + `enabled` tinyint(1) NOT NULL, + `whitelist` longtext NOT NULL, + `blacklist` longtext NOT NULL, + PRIMARY KEY (`id`), + KEY `embargo_ipfilter_16905482` (`changed_by_id`), + CONSTRAINT `changed_by_id_refs_id_3babbf0a22c1f5d3` FOREIGN KEY (`changed_by_id`) REFERENCES `auth_user` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `external_auth_externalauthmap`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; @@ -933,6 +990,18 @@ CREATE TABLE `psychometrics_psychometricdata` ( UNIQUE KEY `studentmodule_id` (`studentmodule_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `reverification_midcoursereverificationwindow`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `reverification_midcoursereverificationwindow` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `course_id` varchar(255) NOT NULL, + `start_date` datetime DEFAULT NULL, + `end_date` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `reverification_midcoursereverificationwindow_ff48d8e5` (`course_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `shoppingcart_certificateitem`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; @@ -969,7 +1038,7 @@ CREATE TABLE `shoppingcart_order` ( `bill_to_ccnum` varchar(8) NOT NULL, `bill_to_cardtype` varchar(32) NOT NULL, `processor_reply_dump` longtext NOT NULL, - `refunded_time` datetime DEFAULT NULL, + `refunded_time` datetime, PRIMARY KEY (`id`), KEY `shoppingcart_order_fbfc09f1` (`user_id`), CONSTRAINT `user_id_refs_id_a4b0342e1195673` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`) @@ -987,9 +1056,9 @@ CREATE TABLE `shoppingcart_orderitem` ( `unit_cost` decimal(30,2) NOT NULL, `line_desc` varchar(1024) NOT NULL, `currency` varchar(8) NOT NULL, - `fulfilled_time` datetime DEFAULT NULL, + `fulfilled_time` datetime, `report_comments` longtext NOT NULL, - `refund_requested_time` datetime DEFAULT NULL, + `refund_requested_time` datetime, `service_fee` decimal(30,2) NOT NULL, PRIMARY KEY (`id`), KEY `shoppingcart_orderitem_8337030b` (`order_id`), @@ -1034,7 +1103,25 @@ CREATE TABLE `south_migrationhistory` ( `migration` varchar(255) NOT NULL, `applied` datetime NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=114 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=125 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `splash_splashconfig`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `splash_splashconfig` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `change_date` datetime NOT NULL, + `changed_by_id` int(11) DEFAULT NULL, + `enabled` tinyint(1) NOT NULL, + `cookie_name` longtext NOT NULL, + `cookie_allowed_values` longtext NOT NULL, + `unaffected_usernames` longtext NOT NULL, + `redirect_url` varchar(200) NOT NULL, + `unaffected_url_paths` longtext NOT NULL, + PRIMARY KEY (`id`), + KEY `splash_splashconfig_16905482` (`changed_by_id`), + CONSTRAINT `changed_by_id_refs_id_6024c0b79125b21c` FOREIGN KEY (`changed_by_id`) REFERENCES `auth_user` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `student_anonymoususerid`; /*!40101 SET @saved_cs_client = @@character_set_client */; @@ -1085,6 +1172,19 @@ CREATE TABLE `student_courseenrollmentallowed` ( KEY `student_courseenrollmentallowed_3216ff68` (`created`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `student_loginfailures`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `student_loginfailures` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `failure_count` int(11) NOT NULL, + `lockout_until` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `student_loginfailures_fbfc09f1` (`user_id`), + CONSTRAINT `user_id_refs_id_50dcb1c1e6a71045` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `student_pendingemailchange`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; @@ -1167,7 +1267,7 @@ CREATE TABLE `track_trackinglog` ( `event_type` varchar(512) NOT NULL, `event` longtext NOT NULL, `agent` varchar(256) NOT NULL, - `page` varchar(512) DEFAULT NULL, + `page` varchar(512), `time` datetime NOT NULL, `host` varchar(64) NOT NULL, PRIMARY KEY (`id`) @@ -1208,6 +1308,8 @@ CREATE TABLE `verify_student_softwaresecurephotoverification` ( `error_msg` longtext NOT NULL, `error_code` varchar(50) NOT NULL, `photo_id_key` longtext NOT NULL, + `window_id` int(11), + `display` tinyint(1) NOT NULL, PRIMARY KEY (`id`), KEY `verify_student_softwaresecurephotoverification_fbfc09f1` (`user_id`), KEY `verify_student_softwaresecurephotoverification_8713c555` (`receipt_id`), @@ -1215,8 +1317,11 @@ CREATE TABLE `verify_student_softwaresecurephotoverification` ( KEY `verify_student_softwaresecurephotoverification_f84f7de6` (`updated_at`), KEY `verify_student_softwaresecurephotoverification_4452d192` (`submitted_at`), KEY `verify_student_softwaresecurephotoverification_b2c165b4` (`reviewing_user_id`), + KEY `verify_student_softwaresecurephotoverification_7343ffda` (`window_id`), + KEY `verify_student_softwaresecurephotoverification_35eebcb6` (`display`), CONSTRAINT `reviewing_user_id_refs_id_5b90d52ad6ea4207` FOREIGN KEY (`reviewing_user_id`) REFERENCES `auth_user` (`id`), - CONSTRAINT `user_id_refs_id_5b90d52ad6ea4207` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`) + CONSTRAINT `user_id_refs_id_5b90d52ad6ea4207` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`), + CONSTRAINT `window_id_refs_id_30f70c30fce8f38a` FOREIGN KEY (`window_id`) REFERENCES `reverification_midcoursereverificationwindow` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `waffle_flag`; @@ -1448,9 +1553,9 @@ DROP TABLE IF EXISTS `wiki_imagerevision`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `wiki_imagerevision` ( `revisionpluginrevision_ptr_id` int(11) NOT NULL, - `image` varchar(2000) DEFAULT NULL, - `width` smallint(6) DEFAULT NULL, - `height` smallint(6) DEFAULT NULL, + `image` varchar(2000), + `width` smallint(6), + `height` smallint(6), PRIMARY KEY (`revisionpluginrevision_ptr_id`), CONSTRAINT `revisionpluginrevision_ptr_id_refs_id_5da3ee545b9fc791` FOREIGN KEY (`revisionpluginrevision_ptr_id`) REFERENCES `wiki_revisionpluginrevision` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -1484,7 +1589,7 @@ DROP TABLE IF EXISTS `wiki_revisionplugin`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `wiki_revisionplugin` ( `articleplugin_ptr_id` int(11) NOT NULL, - `current_revision_id` int(11) DEFAULT NULL, + `current_revision_id` int(11), PRIMARY KEY (`articleplugin_ptr_id`), UNIQUE KEY `current_revision_id` (`current_revision_id`), CONSTRAINT `current_revision_id_refs_id_2732d4b244938e26` FOREIGN KEY (`current_revision_id`) REFERENCES `wiki_revisionpluginrevision` (`id`), diff --git a/lms/djangoapps/courseware/tests/test_video_mongo.py b/lms/djangoapps/courseware/tests/test_video_mongo.py index 80849286ea..5b74762b4a 100644 --- a/lms/djangoapps/courseware/tests/test_video_mongo.py +++ b/lms/djangoapps/courseware/tests/test_video_mongo.py @@ -208,8 +208,8 @@ class TestGetHtmlMethod(BaseTestXmodule): context = self.item_descriptor.render('student_view').content expected_context.update({ - 'transcript_languages': '{"en": "English"}' if self.item_descriptor.sub else '{}', - 'transcript_language': 'en' if self.item_descriptor.sub else json.dumps(None), + 'transcript_languages': '{"en": "English"}', + 'transcript_language': 'en', 'transcript_translation_url': self.item_descriptor.xmodule_runtime.handler_url( self.item_descriptor, 'transcript' ).rstrip('/?') + '/translation', diff --git a/lms/envs/common.py b/lms/envs/common.py index a4bcc86452..7a31bd69df 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -221,6 +221,9 @@ FEATURES = { # Hide any Personally Identifiable Information from application logs 'SQUELCH_PII_IN_LOGS': False, + + # Toggle embargo functionality + 'EMBARGO': False, } # Used for A/B testing @@ -259,6 +262,9 @@ node_paths = [ ] NODE_PATH = ':'.join(node_paths) +# For geolocation ip database +GEOIP_PATH = REPO_ROOT / "common/static/data/geoip/GeoIP.dat" + # Where to look for a status message STATUS_MESSAGE_PATH = ENV_ROOT / "status_message.json" @@ -703,6 +709,7 @@ MIDDLEWARE_CLASSES = ( # Allows us to dark-launch particular languages 'dark_lang.middleware.DarkLangMiddleware', + 'embargo.middleware.EmbargoMiddleware', # Allows us to set user preferences # should be after DarkLangMiddleware @@ -727,6 +734,7 @@ MIDDLEWARE_CLASSES = ( # for expiring inactive sessions 'session_inactivity_timeout.middleware.SessionInactivityTimeout', + ) ############################### Pipeline ####################################### @@ -1141,6 +1149,8 @@ INSTALLED_APPS = ( # Student Identity Reverification 'reverification', + + 'embargo', ) ######################### MARKETING SITE ############################### diff --git a/lms/envs/test.py b/lms/envs/test.py index c48fe12c8e..fafa1244ee 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -40,6 +40,9 @@ FEATURES['ENABLE_SHOPPING_CART'] = True FEATURES['ENABLE_S3_GRADE_DOWNLOADS'] = True FEATURES['ALLOW_COURSE_STAFF_GRADE_DOWNLOADS'] = True +# Toggles embargo on for testing +FEATURES['EMBARGO'] = True + # Need wiki for courseware views to work. TODO (vshnayder): shouldn't need it. WIKI_ENABLED = True diff --git a/lms/templates/static_templates/embargo.html b/lms/templates/static_templates/embargo.html new file mode 100644 index 0000000000..23ebd75a71 --- /dev/null +++ b/lms/templates/static_templates/embargo.html @@ -0,0 +1,8 @@ +<%! from django.utils.translation import ugettext as _ %> +<%inherit file="../main.html" /> + +<%block name="pagetitle">${_("This Course Unavailable In Your Country")} + +
+

${_("Our system indicates that you are trying to access an edX course from an IP address associated with a country currently subjected to U.S. economic and trade sanctions. Unfortunately, at this time edX must comply with export controls, and we cannot allow you to access this particular course. Feel free to browse our catalogue to find other courses you may be interested in taking.")}

+
diff --git a/lms/urls.py b/lms/urls.py index 9f66578156..bcda5d4c51 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -11,7 +11,6 @@ if settings.DEBUG or settings.FEATURES.get('ENABLE_DJANGO_ADMIN_SITE'): urlpatterns = ('', # nopep8 # certificate view - url(r'^update_certificate$', 'certificates.views.update_certificate'), url(r'^$', 'branding.views.index', name="root"), # Main marketing page, or redirect to courseware url(r'^dashboard$', 'student.views.dashboard', name="dashboard"), @@ -66,6 +65,8 @@ urlpatterns = ('', # nopep8 url(r'^', include('waffle.urls')), url(r'^i18n/', include('django.conf.urls.i18n')), + + url(r'^embargo$', 'student.views.embargo', name="embargo"), ) # if settings.FEATURES.get("MULTIPLE_ENROLLMENT_ROLES"): diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt index 5caf2ec391..3125e74117 100644 --- a/requirements/edx/github.txt +++ b/requirements/edx/github.txt @@ -13,6 +13,8 @@ -e git+https://github.com/gabrielfalcao/lettuce.git@cccc3978ad2df82a78b6f9648fe2e9baddd22f88#egg=lettuce -e git+https://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev -e git+https://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk +-e git+https://github.com/un33k/django-ipware.git@42cb1bb1dc680a60c6452e8bb2b843c2a0382c90#egg=django-ipware +-e git+https://github.com/appliedsec/pygeoip.git@95e69341cebf5a6a9fbf7c4f5439d458898bdc3b#egg=pygeoip # Our libraries: -e git+https://github.com/edx/XBlock.git@893cd83dfb24405ce81b07f49c1c2e3053cdc865#egg=XBlock