Commit cf5e4c9b by clrux

Merge pull request #11754 from edx/rc/2016-03-08

Release Candidate rc/2016-03-08
parents 00b270c3 efa289ba
......@@ -55,9 +55,9 @@ def see_a_multi_step_component(step, category):
if category == 'HTML':
html_matcher = {
'Text': '\n \n',
'Announcement': '<h3>Announcement Date</h3>',
'Zooming Image Tool': '<h3>Zooming Image Tool</h3>',
'E-text Written in LaTeX': '<h3>Example: E-text page</h3>',
'Announcement': '<h3 class="hd hd-2">Announcement Date</h3>',
'Zooming Image Tool': '<h3 class="hd hd-2">Zooming Image Tool</h3>',
'E-text Written in LaTeX': '<h3 class="hd hd-2">Example: E-text page</h3>',
'Raw HTML': '<p>This template is similar to the Text template. The only difference is',
}
actual_html = world.css_html(selector, index=idx)
......
......@@ -125,6 +125,9 @@ def my_display_name_change_is_persisted_on_save(step):
@step('the problem display name is "(.*)"$')
def verify_problem_display_name(step, name):
"""
name is uppercased because the heading styles are uppercase in css
"""
assert_equal(name, world.browser.find_by_css('.problem-header').text)
......
"""
Script for converting a tar.gz file representing an exported course
to the archive format used by a different version of export.
Sample invocation: ./manage.py export_convert_format mycourse.tar.gz ~/newformat/
"""
import os
from path import Path as path
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings
from tempfile import mkdtemp
import tarfile
import shutil
from openedx.core.lib.extract_tar import safetar_extractall
from xmodule.modulestore.xml_exporter import convert_between_versions
class Command(BaseCommand):
"""
Convert between export formats.
"""
help = 'Convert between versions 0 and 1 of the course export format'
args = '<tar.gz archive file> <output path>'
def handle(self, *args, **options):
"Execute the command"
if len(args) != 2:
raise CommandError("export requires two arguments: <tar.gz file> <output path>")
source_archive = args[0]
output_path = args[1]
# Create temp directories to extract the source and create the target archive.
temp_source_dir = mkdtemp(dir=settings.DATA_DIR)
temp_target_dir = mkdtemp(dir=settings.DATA_DIR)
try:
extract_source(source_archive, temp_source_dir)
desired_version = convert_between_versions(temp_source_dir, temp_target_dir)
# New zip up the target directory.
parts = os.path.basename(source_archive).split('.')
archive_name = path(output_path) / "{source_name}_version_{desired_version}.tar.gz".format(
source_name=parts[0], desired_version=desired_version
)
with open(archive_name, "w"):
tar_file = tarfile.open(archive_name, mode='w:gz')
try:
for item in os.listdir(temp_target_dir):
tar_file.add(path(temp_target_dir) / item, arcname=item)
finally:
tar_file.close()
print "Created archive {0}".format(archive_name)
except ValueError as err:
raise CommandError(err)
finally:
shutil.rmtree(temp_source_dir)
shutil.rmtree(temp_target_dir)
def extract_source(source_archive, target):
"""
Extract the archive into the given target directory.
"""
with tarfile.open(source_archive) as tar_file:
safetar_extractall(tar_file, target)
"""
Test for export_convert_format.
"""
from unittest import TestCase
from django.core.management import call_command, CommandError
from django.conf import settings
from tempfile import mkdtemp
import shutil
from path import Path as path
from contentstore.management.commands.export_convert_format import Command, extract_source
from xmodule.tests.helpers import directories_equal
class ConvertExportFormat(TestCase):
"""
Tests converting between export formats.
"""
def setUp(self):
""" Common setup. """
super(ConvertExportFormat, self).setUp()
self.temp_dir = mkdtemp(dir=settings.DATA_DIR)
self.addCleanup(shutil.rmtree, self.temp_dir)
self.data_dir = path(__file__).realpath().parent / 'data'
self.version0 = self.data_dir / "Version0_drafts.tar.gz"
self.version1 = self.data_dir / "Version1_drafts.tar.gz"
self.command = Command()
def test_no_args(self):
""" Test error condition of no arguments. """
errstring = "export requires two arguments"
with self.assertRaisesRegexp(CommandError, errstring):
self.command.handle()
def test_version1_archive(self):
"""
Smoke test for creating a version 1 archive from a version 0.
"""
call_command('export_convert_format', self.version0, self.temp_dir)
output = path(self.temp_dir) / 'Version0_drafts_version_1.tar.gz'
self.assertTrue(self._verify_archive_equality(output, self.version1))
def test_version0_archive(self):
"""
Smoke test for creating a version 0 archive from a version 1.
"""
call_command('export_convert_format', self.version1, self.temp_dir)
output = path(self.temp_dir) / 'Version1_drafts_version_0.tar.gz'
self.assertTrue(self._verify_archive_equality(output, self.version0))
def _verify_archive_equality(self, file1, file2):
"""
Helper function for determining if 2 archives are equal.
"""
temp_dir_1 = mkdtemp(dir=settings.DATA_DIR)
temp_dir_2 = mkdtemp(dir=settings.DATA_DIR)
try:
extract_source(file1, temp_dir_1)
extract_source(file2, temp_dir_2)
return directories_equal(temp_dir_1, temp_dir_2)
finally:
shutil.rmtree(temp_dir_1)
shutil.rmtree(temp_dir_2)
......@@ -8,6 +8,7 @@ from chrono import Timer
from mock import patch, Mock
import ddt
from django.conf import settings
from django.test import RequestFactory
from django.test.client import Client
......@@ -28,8 +29,9 @@ from opaque_keys.edx.locations import CourseLocator
from xmodule.error_module import ErrorDescriptor
from course_action_state.models import CourseRerunState
TOTAL_COURSES_COUNT = 500
USER_COURSES_COUNT = 50
TOTAL_COURSES_COUNT = 10
USER_COURSES_COUNT = 1
@ddt.ddt
......@@ -99,6 +101,15 @@ class TestCourseListing(ModuleStoreTestCase, XssTestMixin):
self.assertEqual(response.status_code, 200)
self.assert_no_xss(response, escaping_content)
def test_empty_course_listing(self):
"""
Test on empty course listing, studio name is properly displayed
"""
message = "Are you staff on an existing {studio_name} course?".format(studio_name=settings.STUDIO_SHORT_NAME)
response = self.client.get('/home')
self.assertEqual(response.status_code, 200)
self.assertIn(message, response.content)
def test_get_course_list(self):
"""
Test getting courses with new access group format e.g. 'instructor_edx.course.run'
......@@ -147,8 +158,8 @@ class TestCourseListing(ModuleStoreTestCase, XssTestMixin):
self.assertEqual(courses_list_by_groups, [])
@ddt.data(
(ModuleStoreEnum.Type.split, 5),
(ModuleStoreEnum.Type.mongo, 3)
(ModuleStoreEnum.Type.split, 3),
(ModuleStoreEnum.Type.mongo, 2)
)
@ddt.unpack
def test_staff_course_listing(self, default_store, mongo_calls):
......@@ -255,8 +266,8 @@ class TestCourseListing(ModuleStoreTestCase, XssTestMixin):
)
@ddt.data(
(ModuleStoreEnum.Type.split, 150, 505),
(ModuleStoreEnum.Type.mongo, USER_COURSES_COUNT, 3)
(ModuleStoreEnum.Type.split, 3, 13),
(ModuleStoreEnum.Type.mongo, USER_COURSES_COUNT, 2)
)
@ddt.unpack
def test_course_listing_performance(self, store, courses_list_from_group_calls, courses_list_calls):
......
......@@ -2,6 +2,7 @@
Unit tests for course import and export
"""
import copy
import ddt
import json
import logging
import lxml
......@@ -15,20 +16,24 @@ from uuid import uuid4
from django.test.utils import override_settings
from django.conf import settings
from xmodule.contentstore.django import contentstore
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.xml_exporter import export_library_to_xml
from xmodule.modulestore.xml_importer import import_library_from_xml
from xmodule.modulestore import LIBRARY_ROOT
from xmodule.modulestore import LIBRARY_ROOT, ModuleStoreEnum
from contentstore.utils import reverse_course_url
from contentstore.tests.utils import CourseTestCase
from xmodule.modulestore.tests.factories import ItemFactory, LibraryFactory
from xmodule.modulestore.tests.utils import (
MongoContentstoreBuilder, SPLIT_MODULESTORE_SETUP, TEST_DATA_DIR
)
from opaque_keys.edx.locator import LibraryLocator
from contentstore.tests.utils import CourseTestCase
from openedx.core.lib.extract_tar import safetar_extractall
from student import auth
from student.roles import CourseInstructorRole, CourseStaffRole
from models.settings.course_metadata import CourseMetadata
from util import milestones_helpers
from xmodule.modulestore.django import modulestore
from milestones.tests.utils import MilestonesTestCaseMixin
TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE)
......@@ -123,6 +128,7 @@ class ImportEntranceExamTestCase(CourseTestCase, MilestonesTestCaseMixin):
self.assertEquals(course.entrance_exam_minimum_score_pct, 0.7)
@ddt.ddt
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
class ImportTestCase(CourseTestCase):
"""
......@@ -435,6 +441,73 @@ class ImportTestCase(CourseTestCase):
self.assertIn(test_block3.url_name, children)
self.assertIn(test_block4.url_name, children)
@ddt.data(
ModuleStoreEnum.Branch.draft_preferred,
ModuleStoreEnum.Branch.published_only,
)
def test_library_import_branch_settings(self, branch_setting):
"""
Try importing a known good library archive under either branch setting.
The branch setting should have no effect on library import.
"""
with self.store.branch_setting(branch_setting):
library = LibraryFactory.create(modulestore=self.store)
lib_key = library.location.library_key
extract_dir = path(tempfile.mkdtemp(dir=settings.DATA_DIR))
# the extract_dir needs to be passed as a relative dir to
# import_library_from_xml
extract_dir_relative = path.relpath(extract_dir, settings.DATA_DIR)
try:
with tarfile.open(path(TEST_DATA_DIR) / 'imports' / 'library.HhJfPD.tar.gz') as tar:
safetar_extractall(tar, extract_dir)
import_library_from_xml(
self.store,
self.user.id,
settings.GITHUB_REPO_ROOT,
[extract_dir_relative / 'library'],
load_error_modules=False,
static_content_store=contentstore(),
target_id=lib_key
)
finally:
shutil.rmtree(extract_dir)
@ddt.data(
ModuleStoreEnum.Branch.draft_preferred,
ModuleStoreEnum.Branch.published_only,
)
def test_library_import_branch_settings_again(self, branch_setting):
# Construct the contentstore for storing the import
with MongoContentstoreBuilder().build() as source_content:
# Construct the modulestore for storing the import (using the previously created contentstore)
with SPLIT_MODULESTORE_SETUP.build(contentstore=source_content) as source_store:
# Use the test branch setting.
with source_store.branch_setting(branch_setting):
source_library_key = LibraryLocator(org='TestOrg', library='TestProbs')
extract_dir = path(tempfile.mkdtemp(dir=settings.DATA_DIR))
# the extract_dir needs to be passed as a relative dir to
# import_library_from_xml
extract_dir_relative = path.relpath(extract_dir, settings.DATA_DIR)
try:
with tarfile.open(path(TEST_DATA_DIR) / 'imports' / 'library.HhJfPD.tar.gz') as tar:
safetar_extractall(tar, extract_dir)
import_library_from_xml(
source_store,
self.user.id,
settings.GITHUB_REPO_ROOT,
[extract_dir_relative / 'library'],
static_content_store=source_content,
target_id=source_library_key,
load_error_modules=False,
raise_on_failure=True,
create_if_not_present=True,
)
finally:
shutil.rmtree(extract_dir)
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
class ExportTestCase(CourseTestCase):
......@@ -556,3 +629,59 @@ class ExportTestCase(CourseTestCase):
)
self.test_export_targz_urlparam()
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
class TestLibraryImportExport(CourseTestCase):
"""
Tests for importing content libraries from XML and exporting them to XML.
"""
def setUp(self):
super(TestLibraryImportExport, self).setUp()
self.export_dir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, self.export_dir, ignore_errors=True)
def test_content_library_export_import(self):
library1 = LibraryFactory.create(modulestore=self.store)
source_library1_key = library1.location.library_key
library2 = LibraryFactory.create(modulestore=self.store)
source_library2_key = library2.location.library_key
import_library_from_xml(
self.store,
'test_user',
TEST_DATA_DIR,
['library_empty_problem'],
static_content_store=contentstore(),
target_id=source_library1_key,
load_error_modules=False,
raise_on_failure=True,
create_if_not_present=True,
)
export_library_to_xml(
self.store,
contentstore(),
source_library1_key,
self.export_dir,
'exported_source_library',
)
source_library = self.store.get_library(source_library1_key)
self.assertEqual(source_library.url_name, 'library')
# Import the exported library into a different content library.
import_library_from_xml(
self.store,
'test_user',
self.export_dir,
['exported_source_library'],
static_content_store=contentstore(),
target_id=source_library2_key,
load_error_modules=False,
raise_on_failure=True,
create_if_not_present=True,
)
# Compare the two content libraries for equality.
self.assertCoursesEqual(source_library1_key, source_library2_key)
......@@ -10,7 +10,7 @@ from provider.constants import CONFIDENTIAL
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin, ProgramsDataMixin
from openedx.core.djangolib.markup import escape
from openedx.core.djangolib.markup import Text
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
......@@ -64,7 +64,7 @@ class TestProgramListing(ProgramsApiConfigMixin, ProgramsDataMixin, SharedModule
self.mock_programs_api(data={'results': []})
response = self.client.get(self.studio_home)
self.assertIn(escape("You haven't created any programs yet."), response.content)
self.assertIn(Text("You haven't created any programs yet."), response.content)
# When data is provided, expect a program listing.
self.mock_programs_api()
......
......@@ -80,6 +80,14 @@ DATABASES = {
'timeout': 30,
},
'ATOMIC_REQUESTS': True,
},
'student_module_history': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': TEST_ROOT / "db" / "test_student_module_history.db",
'TEST_NAME': TEST_ROOT / "db" / "test_student_module_history.db",
'OPTIONS': {
'timeout': 30,
},
}
}
......
......@@ -274,12 +274,6 @@ else:
DATABASES = AUTH_TOKENS['DATABASES']
# Enable automatic transaction management on all databases
# https://docs.djangoproject.com/en/1.8/topics/db/transactions/#tying-transactions-to-http-requests
# This needs to be true for all databases
for database_name in DATABASES:
DATABASES[database_name]['ATOMIC_REQUESTS'] = True
MODULESTORE = convert_module_store_setting_if_needed(AUTH_TOKENS.get('MODULESTORE', MODULESTORE))
CONTENTSTORE = AUTH_TOKENS['CONTENTSTORE']
DOC_STORE_CONFIG = AUTH_TOKENS['DOC_STORE_CONFIG']
......
......@@ -13,18 +13,27 @@ from .aws import *
import os
from django.core.exceptions import ImproperlyConfigured
DB_OVERRIDES = dict(
PASSWORD=os.environ.get('DB_MIGRATION_PASS', None),
ENGINE=os.environ.get('DB_MIGRATION_ENGINE', DATABASES['default']['ENGINE']),
USER=os.environ.get('DB_MIGRATION_USER', DATABASES['default']['USER']),
NAME=os.environ.get('DB_MIGRATION_NAME', DATABASES['default']['NAME']),
HOST=os.environ.get('DB_MIGRATION_HOST', DATABASES['default']['HOST']),
PORT=os.environ.get('DB_MIGRATION_PORT', DATABASES['default']['PORT']),
)
if DB_OVERRIDES['PASSWORD'] is None:
raise ImproperlyConfigured("No database password was provided for running "
"migrations. This is fatal.")
def get_db_overrides(db_name):
"""
Now that we have multiple databases, we want to look up from the environment
for both databases.
"""
db_overrides = dict(
PASSWORD=os.environ.get('DB_MIGRATION_PASS', None),
ENGINE=os.environ.get('DB_MIGRATION_ENGINE', DATABASES[db_name]['ENGINE']),
USER=os.environ.get('DB_MIGRATION_USER', DATABASES[db_name]['USER']),
NAME=os.environ.get('DB_MIGRATION_NAME', DATABASES[db_name]['NAME']),
HOST=os.environ.get('DB_MIGRATION_HOST', DATABASES[db_name]['HOST']),
PORT=os.environ.get('DB_MIGRATION_PORT', DATABASES[db_name]['PORT']),
)
for override, value in DB_OVERRIDES.iteritems():
DATABASES['default'][override] = value
if db_overrides['PASSWORD'] is None:
raise ImproperlyConfigured("No database password was provided for running "
"migrations. This is fatal.")
return db_overrides
for db in DATABASES:
# You never migrate a read_replica
if db != 'read_replica':
DATABASES[db].update(get_db_overrides(db))
......@@ -30,6 +30,14 @@
"PASSWORD": "",
"PORT": "3306",
"USER": "root"
},
"student_module_history": {
"ENGINE": "django.db.backends.mysql",
"HOST": "localhost",
"NAME": "student_module_history_test",
"PASSWORD": "",
"PORT": "3306",
"USER": "root"
}
},
"DOC_STORE_CONFIG": {
......
......@@ -1114,6 +1114,11 @@ PROCTORING_BACKEND_PROVIDER = {
}
PROCTORING_SETTINGS = {}
############################ Global Database Configuration #####################
DATABASE_ROUTERS = [
'openedx.core.lib.django_courseware_routers.StudentModuleHistoryExtendedRouter',
]
############################ OAUTH2 Provider ###################################
......
......@@ -23,6 +23,7 @@ import os
from path import Path as path
from warnings import filterwarnings, simplefilter
from uuid import uuid4
from util.db import NoOpMigrationModules
# import settings from LMS for consistent behavior with CMS
# pylint: disable=unused-import
......@@ -42,7 +43,7 @@ MONGO_HOST = os.environ.get('EDXAPP_TEST_MONGO_HOST', 'localhost')
THIS_UUID = uuid4().hex[:5]
# Nose Test Runner
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
TEST_RUNNER = 'openedx.core.djangolib.nose.NoseTestSuiteRunner'
_SYSTEM = 'cms'
......@@ -129,9 +130,10 @@ DATABASES = {
},
}
# This hack disables migrations during tests. We want to create tables directly from the models for speed.
# See https://groups.google.com/d/msg/django-developers/PWPj3etj3-U/kCl6pMsQYYoJ.
MIGRATION_MODULES = {app: "app.migrations_not_used_in_tests" for app in INSTALLED_APPS}
if os.environ.get('DISABLE_MIGRATIONS'):
# Create tables directly from apps' models. This can be removed once we upgrade
# to Django 1.9, which allows setting MIGRATION_MODULES to None in order to skip migrations.
MIGRATION_MODULES = NoOpMigrationModules()
LMS_BASE = "localhost:8000"
FEATURES['PREVIEW_LMS_BASE'] = "preview"
......
......@@ -9,7 +9,11 @@ settings.INSTALLED_APPS # pylint: disable=pointless-statement
from openedx.core.lib.django_startup import autostartup
import django
from monkey_patch import third_party_auth
from monkey_patch import (
third_party_auth,
django_db_models_options,
django_utils_http_is_safe_url
)
import xmodule.x_module
import cms.lib.xblock.runtime
......@@ -22,6 +26,8 @@ def run():
Executed during django startup
"""
third_party_auth.patch()
django_db_models_options.patch()
django_utils_http_is_safe_url.patch()
# Comprehensive theming needs to be set up before django startup,
# because modifying django template paths after startup has no effect.
......
......@@ -75,6 +75,7 @@
// ====================
@import 'xmodule/modules/css/module-styles.scss';
@import 'xmodule/descriptors/css/module-styles.scss';
@import 'xmodule/headings';
@import 'elements/xmodules'; // styling for Studio-specific contexts
@import 'developer'; // used for any developer-created scss that needs further polish/refactoring
......
/*
* This comes from the UXPL, and is modified for use.
* The UXPL isn't available retroactively, so this shims
* the headings from the UXPL with what we're using in
* the platform to better sync things up in the meantime.
* It is scoped to #seq_content, specifically for xblock.
*
* Once the UXPl is fitted retroactively, this can be removed.
*/
$headings-count: 8;
$headings-font-weight-light: 200;
$headings-font-weight-normal: 400;
$headings-font-weight-bold: 600;
$headings-base-font-family: inherit;
$headings-base-color: $gray-d2;
%reset-headings {
margin: 0;
font-weight: $headings-font-weight-normal;
font-size: inherit;
line-height: inherit;
color: $headings-base-color;
}
%hd-1 {
margin-bottom: 1.41575em;
font-size: 2em;
line-height: 1.4em;
}
%hd-2 {
margin-bottom: 1em;
font-size: 1.5em;
font-weight: $headings-font-weight-normal;
line-height: 1.4em;
}
%hd-3 {
margin-bottom: ($baseline / 2);
font-size: 1.35em;
font-weight: $headings-font-weight-normal;
line-height: 1.4em;
}
%hd-4 {
margin-bottom: ($baseline / 2);
font-size: 1.25em;
font-weight: $headings-font-weight-bold;
line-height: 1.4em;
}
%hd-5 {
margin-bottom: ($baseline / 2);
font-size: 1.1em;
font-weight: $headings-font-weight-bold;
line-height: 1.4em;
}
%hd-6 {
margin-bottom: ($baseline / 2);
font-size: 1em;
font-weight: $headings-font-weight-bold;
line-height: 1.4em;
}
%hd-7 {
margin-bottom: ($baseline / 4);
font-size: 14px;
font-weight: $headings-font-weight-bold;
text-transform: uppercase;
line-height: 1.6em;
letter-spacing: 1px;
}
%hd-8 {
margin-bottom: ($baseline / 8);
font-size: 12px;
font-weight: $headings-font-weight-bold;
text-transform: uppercase;
line-height: 1.5em;
letter-spacing: 1px;
}
.wrapper-xblock .xblock-render .xblock .xblock-render .xblock {
.hd-1,
.hd-2,
.hd-3,
.hd-4,
.hd-5,
.hd-6,
.hd-7,
.hd-8 {
@extend %reset-headings;
}
// ----------------------------
// #CANNED
// ----------------------------
// canned heading classes
@for $i from 1 through $headings-count {
.hd-#{$i} {
@extend %hd-#{$i};
}
}
h3 {
@extend %hd-2;
font-weight: $headings-font-weight-normal;
// override external modules and xblocks that use inline CSS
text-transform: initial;
}
}
## coding=utf-8
<%namespace name='static' file='static_content.html'/>
<%!
from openedx.core.djangolib.markup import ugettext as _
from django.utils.translation import ugettext as _
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
)
......
<%page expression_filter="h"/>
<%inherit file="base.html" />
<%def name="online_help_token()">
<%
......@@ -8,27 +9,30 @@ else:
%>
</%def>
<%!
from django.utils.translation import ugettext as _
from contentstore.views.helpers import xblock_studio_url, xblock_type_display_name
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
)
from openedx.core.djangolib.markup import HTML, ugettext as _
from openedx.core.djangolib.markup import Text, HTML
%>
<%block name="title">${xblock.display_name_with_default_escaped} ${xblock_type_display_name(xblock) | h}</%block>
<%block name="title">${xblock.display_name_with_default} ${xblock_type_display_name(xblock)}</%block>
<%block name="bodyclass">is-signedin course container view-container</%block>
<%namespace name='static' file='static_content.html'/>
<%block name="header_extras">
% for template_name in templates:
<script type="text/template" id="${template_name | h}-tpl">
<script type="text/template" id="${template_name}-tpl">
<%static:include path="js/${template_name}.underscore" />
</script>
% endfor
<script type="text/template" id="image-modal-tpl">
<%static:include path="common/templates/image-modal.underscore" />
</script>
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css') | h}" />
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css')}" />
</%block>
<%block name="requirejs">
......@@ -57,15 +61,15 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
ancestor_url = xblock_studio_url(ancestor)
%>
% if ancestor_url:
<a href="${ancestor_url | h}" class="navigation-item navigation-link navigation-parent">${ancestor.display_name_with_default_escaped | h}</a>
<a href="${ancestor_url}" class="navigation-item navigation-link navigation-parent">${ancestor.display_name_with_default}</a>
% else:
<span class="navigation-item navigation-parent">${ancestor.display_name_with_default_escaped | h}</span>
<span class="navigation-item navigation-parent">${ancestor.display_name_with_default}</span>
% endif
% endfor
</small>
<div class="wrapper-xblock-field incontext-editor is-editable"
data-field="display_name" data-field-display-name="${_("Display Name")}">
<h1 class="page-header-title xblock-field-value incontext-editor-value"><span class="title-value">${xblock.display_name_with_default_escaped | h}</span></h1>
<h1 class="page-header-title xblock-field-value incontext-editor-value"><span class="title-value">${xblock.display_name_with_default}</span></h1>
</div>
</div>
......@@ -74,12 +78,12 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
<ul>
% if is_unit_page:
<li class="action-item action-view nav-item">
<a href="${published_preview_link | h}" class="button button-view action-button is-disabled" aria-disabled="true" rel="external" title="${_('Open the courseware in the LMS')}">
<a href="${published_preview_link}" class="button button-view action-button is-disabled" aria-disabled="true" rel="external" title="${_('Open the courseware in the LMS')}">
<span class="action-button-text">${_("View Live Version")}</span>
</a>
</li>
<li class="action-item action-preview nav-item">
<a href="${draft_preview_link | h}" class="button button-preview action-button" rel="external" title="${_('Preview the courseware in the LMS')}">
<a href="${draft_preview_link}" class="button button-preview action-button" rel="external" title="${_('Preview the courseware in the LMS')}">
<span class="action-button-text">${_("Preview")}</span>
</a>
</li>
......@@ -102,7 +106,7 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
<article class="content-primary">
<div class="container-message wrapper-message"></div>
<section class="wrapper-xblock level-page is-hidden studio-xblock-wrapper" data-locator="${xblock_locator | h}" data-course-key="${xblock_locator.course_key | h}">
<section class="wrapper-xblock level-page is-hidden studio-xblock-wrapper" data-locator="${xblock_locator}" data-course-key="${xblock_locator.course_key}">
</section>
<div class="ui-loading">
<p><span class="spin"><i class="icon fa fa-refresh"></i></span> <span class="copy">${_("Loading")}</span></p>
......@@ -112,13 +116,13 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
% if xblock.category == 'split_test':
<div class="bit">
<h3 class="title-3">${_("Adding components")}</h3>
<p>${_("Select a component type under {strong_start}Add New Component{strong_end}. Then select a template.").format(
<p>${Text(_("Select a component type under {strong_start}Add New Component{strong_end}. Then select a template.")).format(
strong_start=HTML('<strong>'),
strong_end=HTML("</strong>"),
)}</p>
<p>${_("The new component is added at the bottom of the page or group. You can then edit and move the component.")}</p>
<h3 class="title-3">${_("Editing components")}</h3>
<p>${_("Click the {strong_start}Edit{strong_end} icon in a component to edit its content.").format(
<p>${Text(_("Click the {strong_start}Edit{strong_end} icon in a component to edit its content.")).format(
strong_start=HTML('<strong>'),
strong_end=HTML("</strong>"),
)}</p>
......@@ -129,7 +133,7 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
<p>${_("Confirm that you have properly configured content in each of your experiment groups.")}</p>
</div>
<div class="bit external-help">
<a href="${get_online_help_info(online_help_token())['doc_url'] | h}" target="_blank" class="button external-help-button">${_("Learn more about component containers")}</a>
<a href="${get_online_help_info(online_help_token())['doc_url']}" target="_blank" class="button external-help-button">${_("Learn more about component containers")}</a>
</div>
% elif is_unit_page:
<div id="publish-unit"></div>
......@@ -139,7 +143,7 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
<div class="wrapper-unit-id bar-mod-content">
<h5 class="title">${_("Location ID")}</h5>
<p class="unit-id">
<span class="unit-id-value" id="unit-location-id-input">${unit.location.name | h}</span>
<span class="unit-id-value" id="unit-location-id-input">${unit.location.name}</span>
<span class="tip"><span class="sr">Tip: </span>${_("Use this ID when you create links to this unit from other course content. You enter the ID in the URL field.")}</span>
</p>
</div>
......
<%! from openedx.core.djangolib.markup import HTML, ugettext as _ %>
<%page expression_filter="h"/>
<%!
from django.utils.translation import ugettext as _
from openedx.core.djangolib.markup import Text, HTML
%>
<%inherit file="base.html" />
......@@ -80,7 +84,7 @@
## Translators: This is an example for the name of the organization sponsoring a course, seen when filling out the form to create a new course. The organization name cannot contain spaces.
## Translators: "e.g. UniversityX or OrganizationX" is a placeholder displayed when user put no data into this field.
<input class="new-course-org" id="new-course-org" type="text" name="new-course-org" required placeholder="${_('e.g. UniversityX or OrganizationX')}" aria-describedby="tip-new-course-org tip-error-new-course-org" />
<span class="tip" id="tip-new-course-org">${_("The name of the organization sponsoring the course. {strong_start}Note: The organization name is part of the course URL.{strong_end} This cannot be changed, but you can set a different display name in Advanced Settings later.").format(
<span class="tip" id="tip-new-course-org">${Text(_("The name of the organization sponsoring the course. {strong_start}Note: The organization name is part of the course URL.{strong_end} This cannot be changed, but you can set a different display name in Advanced Settings later.")).format(
strong_start=HTML('<strong>'),
strong_end=HTML('</strong>'),
)}</span>
......@@ -93,7 +97,7 @@
## seen when filling out the form to create a new course. The number here is
## short for "Computer Science 101". It can contain letters but cannot contain spaces.
<input class="new-course-number" id="new-course-number" type="text" name="new-course-number" required placeholder="${_('e.g. CS101')}" aria-describedby="tip-new-course-number tip-error-new-course-number" />
<span class="tip" id="tip-new-course-number">${_("The unique number that identifies your course within your organization. {strong_start}Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.{strong_end}").format(
<span class="tip" id="tip-new-course-number">${Text(_("The unique number that identifies your course within your organization. {strong_start}Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.{strong_end}")).format(
strong_start=HTML('<strong>'),
strong_end=HTML('</strong>'),
)}</span>
......@@ -105,7 +109,7 @@
## Translators: This is an example for the "run" used to identify different
## instances of a course, seen when filling out the form to create a new course.
<input class="new-course-run" id="new-course-run" type="text" name="new-course-run" required placeholder="${_('e.g. 2014_T1')}" aria-describedby="tip-new-course-run tip-error-new-course-run" />
<span class="tip" id="tip-new-course-run">${_("The term in which your course will run. {strong_start}Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.{strong_end}").format(
<span class="tip" id="tip-new-course-run">${Text(_("The term in which your course will run. {strong_start}Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.{strong_end}")).format(
strong_start=HTML('<strong>'),
strong_end=HTML('</strong>'),
)}</span>
......@@ -165,7 +169,7 @@
## for "Computer Science Problems". The example number may contain letters
## but must not contain spaces.
<input class="new-library-number" id="new-library-number" type="text" name="new-library-number" required placeholder="${_('e.g. CSPROB')}" aria-describedby="tip-new-library-number tip-error-new-library-number" />
<span class="tip" id="tip-new-library-number">${_("The unique code that identifies this library. {strong_start}Note: This is part of your library URL, so no spaces or special characters are allowed.{strong_end} This cannot be changed.").format(
<span class="tip" id="tip-new-library-number">${Text(_("The unique code that identifies this library. {strong_start}Note: This is part of your library URL, so no spaces or special characters are allowed.{strong_end} This cannot be changed.")).format(
strong_start=HTML('<strong>'),
strong_end=HTML('</strong>'),
)}</span>
......@@ -228,7 +232,7 @@
</div>
<div class="status-message">
<p class="copy">${_('The new course will be added to your course list in 5-10 minutes. Return to this page or {link_start}refresh it{link_end} to update the course list. The new course will need some manual configuration.').format(
<p class="copy">${Text(_('The new course will be added to your course list in 5-10 minutes. Return to this page or {link_start}refresh it{link_end} to update the course list. The new course will need some manual configuration.')).format(
link_start=HTML('<a href="#" class="action-reload">'),
link_end=HTML('</a>'),
)}</p>
......@@ -346,7 +350,7 @@
<div class="notice notice-incontext notice-instruction notice-instruction-nocourses list-notices courses-tab active">
<div class="notice-item">
<div class="msg">
<h3 class="title">${_("Are you staff on an existing {studio_name} course?").format(studio_name=set)}</h3>
<h3 class="title">${_("Are you staff on an existing {studio_name} course?").format(studio_name=settings.STUDIO_SHORT_NAME)}</h3>
<div class="copy">
<p>${_('The course creator must give you access to the course. Contact the course creator or administrator for the course you are helping to author.')}</p>
</div>
......@@ -575,7 +579,7 @@
% if course_creator_status=='disallowed_for_this_site' and settings.FEATURES.get('STUDIO_REQUEST_EMAIL',''):
<div class="bit">
<h3 class="title title-3">${_("Can I create courses in {studio_name}?").format(studio_name=settings.STUDIO_NAME)}</h3>
<p>${_("In order to create courses in {studio_name}, you must {link_start}contact {platform_name} staff to help you create a course{link_end}.").format(
<p>${Text(_("In order to create courses in {studio_name}, you must {link_start}contact {platform_name} staff to help you create a course{link_end}.")).format(
studio_name=settings.STUDIO_NAME,
platform_name=settings.PLATFORM_NAME,
link_start=HTML('<a href="mailto:{email}">').format(email=settings.FEATURES.get('STUDIO_REQUEST_EMAIL','')),
......@@ -593,7 +597,7 @@
% elif course_creator_status == "denied":
<div class="bit">
<h3 class="title title-3">${_("Can I create courses in {studio_name}?").format(studio_name=settings.STUDIO_NAME)}</h3>
<p>${_("Your request to author courses in {studio_name} has been denied. Please {link_start}contact {platform_name} Staff with further questions{link_end}.").format(
<p>${Text(_("Your request to author courses in {studio_name} has been denied. Please {link_start}contact {platform_name} Staff with further questions{link_end}.")).format(
studio_name=settings.STUDIO_NAME,
platform_name=settings.PLATFORM_NAME,
link_start=HTML('<a href="mailto:{email}">').format(email=settings.TECH_SUPPORT_EMAIL),
......
......@@ -336,7 +336,7 @@
<section id="problem_i4x-AndyA-ABT101-problem-46d2b65d793549e2876729d55df9a2cb" class="problems-wrapper" data-problem-id="i4x://AndyA/ABT101/problem/46d2b65d793549e2876729d55df9a2cb" data-url="/preview/xblock/i4x:;_;_AndyA;_ABT101;_problem;_46d2b65d793549e2876729d55df9a2cb/handler/xmodule_handler" data-progress_status="none" data-progress_detail="0/1">
<h3 class="problem-header">
<h3 class="hd hd-2 problem-header">
Multiple Choice
</h3>
......
......@@ -19,6 +19,8 @@ from django.template.context import _builtin_context_processors
from django.utils.module_loading import import_string
from util.request import safe_get_host
from request_cache.middleware import RequestCache
REQUEST_CONTEXT = threading.local()
......@@ -51,6 +53,12 @@ def get_template_request_context():
request = getattr(REQUEST_CONTEXT, "request", None)
if not request:
return None
request_cache_dict = RequestCache.get_request_cache().data
cache_key = "edxmako_request_context"
if cache_key in request_cache_dict:
return request_cache_dict[cache_key]
context = RequestContext(request)
context['is_secure'] = request.is_secure()
context['site'] = safe_get_host(request)
......@@ -62,4 +70,6 @@ def get_template_request_context():
for processor in get_template_context_processors():
context.update(processor(request))
request_cache_dict[cache_key] = context
return context
......@@ -33,7 +33,6 @@ def marketing_link(name):
possible URLs for certain links. This function is to decides
which URL should be provided.
"""
# link_map maps URLs from the marketing site to the old equivalent on
# the Django site
link_map = settings.MKTG_URL_LINK_MAP
......
......@@ -290,9 +290,8 @@ class BaseMicrositeBackend(AbstractBaseMicrositeBackend):
in non-mako templates must be loaded before the django startup
"""
microsites_root = settings.MICROSITE_ROOT_DIR
microsite_config_dict = settings.MICROSITE_CONFIGURATION
if microsite_config_dict:
if self.has_configuration_set():
settings.DEFAULT_TEMPLATE_ENGINE['DIRS'].append(microsites_root)
......
"""
Test Microsite base backends.
"""
import logging
from mock import patch
from django.conf import settings
from django.test import TestCase
from microsite_configuration import microsite
from microsite_configuration.backends.base import (
AbstractBaseMicrositeBackend,
BaseMicrositeBackend
)
log = logging.getLogger(__name__)
class NullBackend(AbstractBaseMicrositeBackend):
"""
......@@ -130,3 +138,36 @@ class AbstractBaseMicrositeBackendTests(TestCase):
with self.assertRaises(NotImplementedError):
backend.get_all_orgs()
@patch(
'microsite_configuration.microsite.BACKEND',
microsite.get_backend(
'microsite_configuration.backends.base.BaseMicrositeBackend', BaseMicrositeBackend
)
)
class BaseMicrositeBackendTests(TestCase):
"""
Go through and test the BaseMicrositeBackend class for behavior which is not
overriden in subclasses
"""
def test_enable_microsites_pre_startup(self):
"""
Tests microsite.test_enable_microsites_pre_startup works as expected.
"""
# remove microsite root directory paths first
settings.DEFAULT_TEMPLATE_ENGINE['DIRS'] = [
path for path in settings.DEFAULT_TEMPLATE_ENGINE['DIRS']
if path != settings.MICROSITE_ROOT_DIR
]
with patch('microsite_configuration.backends.base.BaseMicrositeBackend.has_configuration_set',
return_value=False):
microsite.enable_microsites_pre_startup(log)
self.assertNotIn(settings.MICROSITE_ROOT_DIR,
settings.DEFAULT_TEMPLATE_ENGINE['DIRS'])
with patch('microsite_configuration.backends.base.BaseMicrositeBackend.has_configuration_set',
return_value=True):
microsite.enable_microsites_pre_startup(log)
self.assertIn(settings.MICROSITE_ROOT_DIR,
settings.DEFAULT_TEMPLATE_ENGINE['DIRS'])
......@@ -105,22 +105,6 @@ class DatabaseMicrositeBackendTests(DatabaseMicrositeTestCase):
microsite.clear()
self.assertIsNone(microsite.get_value('platform_name'))
def test_enable_microsites_pre_startup(self):
"""
Tests microsite.test_enable_microsites_pre_startup works as expected.
"""
# remove microsite root directory paths first
settings.DEFAULT_TEMPLATE_ENGINE['DIRS'] = [
path for path in settings.DEFAULT_TEMPLATE_ENGINE['DIRS']
if path != settings.MICROSITE_ROOT_DIR
]
with patch.dict('django.conf.settings.FEATURES', {'USE_MICROSITES': False}):
microsite.enable_microsites_pre_startup(log)
self.assertNotIn(settings.MICROSITE_ROOT_DIR, settings.DEFAULT_TEMPLATE_ENGINE['DIRS'])
with patch.dict('django.conf.settings.FEATURES', {'USE_MICROSITES': True}):
microsite.enable_microsites_pre_startup(log)
self.assertIn(settings.MICROSITE_ROOT_DIR, settings.DEFAULT_TEMPLATE_ENGINE['DIRS'])
@patch('edxmako.paths.add_lookup')
def test_enable_microsites(self, add_lookup):
"""
......@@ -173,6 +157,15 @@ class DatabaseMicrositeBackendTests(DatabaseMicrositeTestCase):
with self.assertRaises(Exception):
microsite.set_by_domain('test.microsite2.com')
def test_has_configuration_set(self):
"""
Tests microsite.has_configuration_set works as expected on this backend.
"""
self.assertTrue(microsite.BACKEND.has_configuration_set())
Microsite.objects.all().delete()
self.assertFalse(microsite.BACKEND.has_configuration_set())
@patch(
'microsite_configuration.microsite.TEMPLATES_BACKEND',
......
......@@ -122,6 +122,15 @@ class FilebasedMicrositeBackendTests(TestCase):
microsite.set_by_domain('unknown')
self.assertEqual(microsite.get_value('university'), 'default_university')
def test_has_configuration_set(self):
"""
Tests microsite.has_configuration_set works as expected.
"""
self.assertTrue(microsite.BACKEND.has_configuration_set())
with patch('django.conf.settings.MICROSITE_CONFIGURATION', {}):
self.assertFalse(microsite.BACKEND.has_configuration_set())
@patch(
'microsite_configuration.microsite.TEMPLATES_BACKEND',
......
......@@ -2,6 +2,9 @@
"""
Tests microsite_configuration templatetags and helper functions.
"""
import logging
from mock import patch
from django.test import TestCase
from django.conf import settings
from microsite_configuration.templatetags import microsite as microsite_tags
......@@ -9,6 +12,8 @@ from microsite_configuration import microsite
from microsite_configuration.backends.base import BaseMicrositeBackend
from microsite_configuration.backends.database import DatabaseMicrositeBackend
log = logging.getLogger(__name__)
class MicrositeTests(TestCase):
"""
......@@ -74,3 +79,20 @@ class MicrositeTests(TestCase):
),
DatabaseMicrositeBackend
)
def test_enable_microsites_pre_startup(self):
"""
Tests microsite.test_enable_microsites_pre_startup is not used if the feature is turned off.
"""
# remove microsite root directory paths first
settings.DEFAULT_TEMPLATE_ENGINE['DIRS'] = [
path for path in settings.DEFAULT_TEMPLATE_ENGINE['DIRS']
if path != settings.MICROSITE_ROOT_DIR
]
with patch.dict('django.conf.settings.FEATURES', {'USE_MICROSITES': False}):
microsite.enable_microsites_pre_startup(log)
self.assertNotIn(settings.MICROSITE_ROOT_DIR, settings.DEFAULT_TEMPLATE_ENGINE['DIRS'])
with patch.dict('django.conf.settings.FEATURES', {'USE_MICROSITES': True}):
microsite.enable_microsites_pre_startup(log)
self.assertIn(settings.MICROSITE_ROOT_DIR, settings.DEFAULT_TEMPLATE_ENGINE['DIRS'])
"""
Monkey patch implementation of the following _expire_cache performance improvement:
https://github.com/django/django/commit/7628f87e2b1ab4b8a881f06c8973be4c368aaa3d
Remove once we upgrade to a version of django which includes this fix natively!
NOTE: This is on django's master branch but is NOT currently part of any django 1.8 or 1.9 release.
"""
from django.db.models.options import Options
def patch():
"""
Monkey-patch the Options class.
"""
def _expire_cache(self, forward=True, reverse=True):
# pylint: disable=missing-docstring
# This method is usually called by apps.cache_clear(), when the
# registry is finalized, or when a new field is added.
if forward:
for cache_key in self.FORWARD_PROPERTIES:
if cache_key in self.__dict__:
delattr(self, cache_key)
if reverse and not self.abstract:
for cache_key in self.REVERSE_PROPERTIES:
if cache_key in self.__dict__:
delattr(self, cache_key)
self._get_fields_cache = {} # pylint: disable=protected-access
# Patch constants as a set instead of a list.
Options.FORWARD_PROPERTIES = {'fields', 'many_to_many', 'concrete_fields',
'local_concrete_fields', '_forward_fields_map'}
Options.REVERSE_PROPERTIES = {'related_objects', 'fields_map', '_relation_tree'}
# Patch the expire_cache method to utilize constant's new set data structure.
Options._expire_cache = _expire_cache # pylint: disable=protected-access
"""
Monkey patch the is_safe_url method in django.utils.http for Django 1.8.10.
In that release, the method crashes when a bytestring, non-unicode string is passed-in
as the url.
Remove the monkey patch when the bug is fixed in a Django 1.8 release. Here's the bug:
https://code.djangoproject.com/ticket/26308
"""
from django.utils import http
from django.utils.encoding import force_text
def patch():
"""
Monkey patch the django.utils.http.is_safe_url function to convert the incoming
url and host parameters to unicode.
"""
def create_is_safe_url_wrapper(wrapped_func):
# pylint: disable=missing-docstring
def _wrap_is_safe_url(*args, **kwargs):
def _conv_text(value):
return None if value is None else force_text(value)
return wrapped_func(
# Converted *args.
*tuple(map(_conv_text, args)),
# Converted **kwargs.
**{key: _conv_text(value) for key, value in kwargs.items()}
)
return _wrap_is_safe_url
http.is_safe_url = create_is_safe_url_wrapper(http.is_safe_url)
......@@ -933,7 +933,7 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin):
_id = 0
for course, program_status in data:
programs[unicode(course)] = {
programs[unicode(course)] = [{
'id': _id,
'category': self.category,
'organization': {'display_name': 'Test Organization 1', 'key': 'edX'},
......@@ -958,7 +958,7 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin):
],
'subtitle': 'sub',
'name': self.program_name
}
}]
_id += 1
......@@ -975,7 +975,7 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin):
"""Verify that program data is parsed correctly for a given course"""
with patch('student.views.get_programs_for_dashboard') as mock_data:
mock_data.return_value = {
u'edx/demox/Run_1': {
u'edx/demox/Run_1': [{
'id': 0,
'category': self.category,
'organization': {'display_name': 'Test Organization 1', 'key': 'edX'},
......@@ -984,7 +984,7 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin):
'course_codes': course_codes,
'subtitle': 'sub',
'name': self.program_name
}
}]
}
parse_data = _get_course_programs(
self.user, [
......@@ -998,14 +998,16 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin):
self.assertEqual(
{
u'edx/demox/Run_1': {
'program_id': 0,
'category': 'xseries',
'course_count': len(course_codes),
'display_name': self.program_name,
'program_marketing_url': urljoin(
settings.MKTG_URLS.get('ROOT'), 'xseries' + '/{}'
).format(marketing_slug),
'display_category': 'XSeries'
'display_category': 'XSeries',
'course_program_list': [{
'program_id': 0,
'course_count': len(course_codes),
'display_name': self.program_name,
'program_marketing_url': urljoin(
settings.MKTG_URLS.get('ROOT'), 'xseries' + '/{}'
).format(marketing_slug)
}]
}
},
parse_data
......@@ -1122,8 +1124,9 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin):
self.create_programs_config()
program_data = self._create_program_data([(self.course_1.id, 'active')])
if key_remove and key_remove in program_data[unicode(self.course_1.id)]:
del program_data[unicode(self.course_1.id)][key_remove]
for program in program_data[unicode(self.course_1.id)]:
if key_remove and key_remove in program:
del program[key_remove]
with patch('student.views.get_programs_for_dashboard') as mock_data:
mock_data.return_value = program_data
......@@ -1135,7 +1138,7 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin):
log_warn.assert_called_with(
'Program structure is invalid, skipping display: %r', program_data[
unicode(self.course_1.id)
]
][0]
)
# verify that no programs related upsell messages appear on the
# student dashboard.
......
......@@ -1628,7 +1628,11 @@ def create_account_with_params(request, params):
not do_external_auth
)
# Can't have terms of service for certain SHIB users, like at Stanford
registration_fields = getattr(settings, 'REGISTRATION_EXTRA_FIELDS', {})
tos_required = (
registration_fields.get('terms_of_service') != 'hidden' or
registration_fields.get('honor_code') != 'hidden'
) and (
not settings.FEATURES.get("AUTH_USE_SHIB") or
not settings.FEATURES.get("SHIB_DISABLE_TOS") or
not do_external_auth or
......@@ -2417,26 +2421,29 @@ def _get_course_programs(user, user_enrolled_courses): # pylint: disable=invali
the given user has active enrollments.
Returns:
dict, containing programs keyed by course. Empty if programs cannot be retrieved.
dict, containing programs keyed by course.
"""
course_programs = get_programs_for_dashboard(user, user_enrolled_courses)
programs_data = {}
for course_key, program in course_programs.viewitems():
if program.get('status') == 'active' and program.get('category') == 'xseries':
try:
programs_data[course_key] = {
'course_count': len(program['course_codes']),
'display_name': program['name'],
'category': program.get('category'),
'program_id': program['id'],
'program_marketing_url': urljoin(
settings.MKTG_URLS.get('ROOT'), 'xseries' + '/{}'
).format(program['marketing_slug']),
'display_category': 'XSeries'
}
except KeyError:
log.warning('Program structure is invalid, skipping display: %r', program)
for course_key, programs in course_programs.viewitems():
for program in programs:
if program.get('status') == 'active' and program.get('category') == 'xseries':
try:
programs_for_course = programs_data.setdefault(course_key, {})
programs_for_course.setdefault('course_program_list', []).append({
'course_count': len(program['course_codes']),
'display_name': program['name'],
'program_id': program['id'],
'program_marketing_url': urljoin(
settings.MKTG_URLS.get('ROOT'),
'xseries' + '/{}'
).format(program['marketing_slug'])
})
programs_for_course['display_category'] = 'XSeries'
programs_for_course['category'] = program.get('category')
except KeyError:
log.warning('Program structure is invalid, skipping display: %r', program)
return programs_data
......
......@@ -81,8 +81,6 @@ def initial_setup(server):
desired_capabilities['loggingPrefs'] = {
'browser': 'ALL',
}
elif browser_driver == 'firefox':
desired_capabilities = DesiredCapabilities.FIREFOX
else:
desired_capabilities = {}
......@@ -98,7 +96,13 @@ def initial_setup(server):
# the browser session is invalid, this will
# raise a WebDriverException
try:
world.browser = Browser(browser_driver, desired_capabilities=desired_capabilities)
if browser_driver == 'firefox':
# Lettuce initializes differently for firefox, and sending
# desired_capabilities will not work. So initialize without
# sending desired_capabilities.
world.browser = Browser(browser_driver)
else:
world.browser = Browser(browser_driver, desired_capabilities=desired_capabilities)
world.browser.driver.set_script_timeout(GLOBAL_SCRIPT_TIMEOUT)
world.visit('/')
......
......@@ -231,3 +231,16 @@ def generate_int_id(minimum=0, maximum=MYSQL_MAX_INT, used_ids=None):
cid = random.randint(minimum, maximum)
return cid
class NoOpMigrationModules(object):
"""
Return invalid migrations modules for apps. Used for disabling migrations during tests.
See https://groups.google.com/d/msg/django-developers/PWPj3etj3-U/kCl6pMsQYYoJ.
"""
def __contains__(self, item):
return True
def __getitem__(self, item):
return "notmigrations"
......@@ -2134,6 +2134,10 @@ class StringResponse(LoncapaResponse):
Note: for old code, which supports _or_ separator, we add some backward compatibility handling.
Should be removed soon. When to remove it, is up to Lyla Fisher.
"""
# if given answer is empty.
if not given:
return False
_ = self.capa_system.i18n.ugettext
# backward compatibility, should be removed in future.
if self.backward:
......
......@@ -946,6 +946,13 @@ class StringResponseTest(ResponseTest): # pylint: disable=missing-docstring
hint = correct_map.get_hint('1_2_1')
self.assertEqual(hint, self._get_random_number_result(problem.seed))
def test_empty_answer_graded_as_incorrect(self):
"""
Tests that problem should be graded incorrect if blank space is chosen as answer
"""
problem = self.build_problem(answer=" ", case_sensitive=False, regexp=True)
self.assert_grade(problem, u" ", "incorrect")
class CodeResponseTest(ResponseTest): # pylint: disable=missing-docstring
xml_factory_class = CodeResponseXMLFactory
......
......@@ -399,6 +399,7 @@ class CapaMixin(CapaFields):
'ajax_url': self.runtime.ajax_url,
'progress_status': Progress.to_js_status_str(progress),
'progress_detail': Progress.to_js_detail_str(progress),
'content': self.get_problem_html(encapsulate=False)
})
def check_button_name(self):
......
......@@ -93,11 +93,23 @@ class CapaModule(CapaMixin, XModule):
result = handlers[dispatch](data)
except NotFoundError as err:
_, _, traceback_obj = sys.exc_info()
log.exception(
"Unable to find data when dispatching %s to %s for user %s",
dispatch,
self.scope_ids.usage_id,
self.scope_ids.user_id
)
_, _, traceback_obj = sys.exc_info() # pylint: disable=redefined-outer-name
raise ProcessingError(not_found_error_message), None, traceback_obj
except Exception as err:
_, _, traceback_obj = sys.exc_info()
log.exception(
"Unknown error when dispatching %s to %s for user %s",
dispatch,
self.scope_ids.usage_id,
self.scope_ids.user_id
)
_, _, traceback_obj = sys.exc_info() # pylint: disable=redefined-outer-name
raise ProcessingError(generic_error_message), None, traceback_obj
after = self.get_progress()
......
......@@ -357,8 +357,8 @@ class CourseFields(object):
html_textbooks = List(
display_name=_("HTML Textbooks"),
help=_(
"For HTML textbooks that appear as separate tabs in the courseware, enter the name of the tab (usually "
"the name of the book) as well as the URLs and titles of all the chapters in the book."
"For HTML textbooks that appear as separate tabs in the course, enter the name of the tab (usually "
"the title of the book) as well as the URLs and titles of each chapter in the book."
),
scope=Scope.settings
)
......@@ -423,7 +423,7 @@ class CourseFields(object):
scope=Scope.settings, default=_('Course Handouts'))
show_timezone = Boolean(
help=_(
"True if timezones should be shown on dates in the courseware. "
"True if timezones should be shown on dates in the course. "
"Deprecated in favor of due_date_display_format."
),
scope=Scope.settings, default=True
......@@ -568,7 +568,7 @@ class CourseFields(object):
display_organization = String(
display_name=_("Course Organization Display String"),
help=_(
"Enter the course organization that you want to appear in the courseware. This setting overrides the "
"Enter the course organization that you want to appear in the course. This setting overrides the "
"organization that you entered when you created the course. To use the organization that you entered "
"when you created the course, enter null."
),
......@@ -578,7 +578,7 @@ class CourseFields(object):
display_coursenumber = String(
display_name=_("Course Number Display String"),
help=_(
"Enter the course number that you want to appear in the courseware. This setting overrides the course "
"Enter the course number that you want to appear in the course. This setting overrides the course "
"number that you entered when you created the course. To use the course number that you entered when "
"you created the course, enter null."
),
......
......@@ -13,11 +13,6 @@ $annotatable--body-font-size: em(14);
.annotatable-header {
margin-bottom: .5em;
.annotatable-title {
font-size: em(22);
text-transform: uppercase;
padding: ($baseline/10) ($baseline/5);
}
}
.annotatable-section {
......
<section class='xblock xblock-student_view xmodule_display xmodule_AnnotatableModule' data-type='Annotatable'>
<div class="annotatable-wrapper">
<div class="annotatable-header">
<div class="annotatable-title">First Annotation Exercise</div>
<h3 class="hd hd-2 annotatable-title">First Annotation Exercise</h2>
</div>
<div class="annotatable-section">
<div class="annotatable-section-title">
......@@ -32,4 +32,3 @@
<div class="problem"><a class="annotation-return" href="javascript:void(0)">Return to Annotation</a></div>
<div class="problem"><a class="annotation-return" href="javascript:void(0)">Return to Annotation</a></div>
<div class="problem"><a class="annotation-return" href="javascript:void(0)">Return to Annotation</a></div>
......@@ -8,7 +8,7 @@
<section id="problem_i4x-Me-19_002-problem-Numerical_Input" class="problems-wrapper" data-problem-id="i4x://Me/19.002/problem/Numerical_Input" data-url="/courses/Me/19.002/Test/modx/i4x://Me/19.002/problem/Numerical_Input" data-progress_status="done" data-progress_detail="1/1">
<h3 class="problem-header">
<h3 class="hd hd-2 problem-header">
Numerical Input
</h3>
......
<h3 class="problem-header">Custom Javascript Display and Grading</h3>
<h3 class="hd hd-2 problem-header">Custom Javascript Display and Grading</h3>
<div class="problem">
<div>
......
<h3 class="problem-header">Problem Header</h3>
<h3 class="hd hd-2 problem-header">Problem Header</h3>
<div class='problem-progress'></div>
......
<h3 class="problem-header">Problem Header</h3>
<h3 class="hd hd-2 problem-header">Problem Header</h3>
<div class='problem-progress'></div>
......
......@@ -710,7 +710,7 @@ describe 'MarkdownEditingDescriptor', ->
""")
expect(data).toEqual("""<problem>
<p>Not a header</p>
<h3 class="problem-header">A header</h3>
<h3 class="hd hd-2 problem-header">A header</h3>
<p>Multiple choice w/ parentheticals</p>
<multiplechoiceresponse>
......
......@@ -5,6 +5,7 @@ class @Problem
@id = @el.data('problem-id')
@element_id = @el.attr('id')
@url = @el.data('url')
@content = @el.data('content')
# has_timed_out and has_response are used to ensure that are used to
# ensure that we wait a minimum of ~ 1s before transitioning the check
......@@ -12,7 +13,7 @@ class @Problem
@has_timed_out = false
@has_response = false
@render()
@render(@content)
$: (selector) ->
$(selector, @el)
......
......@@ -202,7 +202,7 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
xml = xml.replace(/\r\n/g, '\n');
// replace headers
xml = xml.replace(/(^.*?$)(?=\n\=\=+$)/gm, '<h3 class="problem-header">$1</h3>');
xml = xml.replace(/(^.*?$)(?=\n\=\=+$)/gm, '<h3 class="hd hd-2 problem-header">$1</h3>');
xml = xml.replace(/\n^\=\=+$/gm, '');
// Pull out demand hints, || a hint ||
......
......@@ -68,6 +68,7 @@ from xblock.core import XBlock
from xblock.fields import Scope, Reference, ReferenceList, ReferenceValueDict
from xmodule.course_module import CourseSummary
from xmodule.errortracker import null_error_tracker
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import (
BlockUsageLocator, DefinitionLocator, CourseLocator, LibraryLocator, VersionTree, LocalId,
)
......@@ -1160,8 +1161,8 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
False - if we want only those items which are in the course tree. This would ensure no orphans are
fetched.
"""
if not isinstance(course_locator, CourseLocator) or course_locator.deprecated:
# The supplied CourseKey is of the wrong type, so it can't possibly be stored in this modulestore.
if not isinstance(course_locator, CourseKey) or course_locator.deprecated:
# The supplied courselike key is of the wrong type, so it can't possibly be stored in this modulestore.
return []
course = self._lookup_course(course_locator)
......
......@@ -544,9 +544,11 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli
block_id = self.DEFAULT_ROOT_LIBRARY_BLOCK_ID
new_usage_key = course_key.make_usage_key(block_type, block_id)
# Only the course import process calls import_xblock(). If the branch setting is published_only,
# then the non-draft blocks are being imported.
if self.get_branch_setting() == ModuleStoreEnum.Branch.published_only:
# Both the course and library import process calls import_xblock().
# If importing a course -and- the branch setting is published_only,
# then the non-draft course blocks are being imported.
is_course = isinstance(course_key, CourseLocator)
if is_course and self.get_branch_setting() == ModuleStoreEnum.Branch.published_only:
# Override any existing drafts (PLAT-297, PLAT-299). This import/publish step removes
# any local changes during the course import.
draft_course = course_key.for_branch(ModuleStoreEnum.BranchName.draft)
......
......@@ -4,6 +4,7 @@ Modulestore configuration for test cases.
"""
import functools
from uuid import uuid4
from contextlib import contextmanager
from mock import patch
......@@ -265,11 +266,14 @@ class SharedModuleStoreTestCase(TestCase):
for Django ORM models that will get cleaned up properly.
"""
MODULESTORE = mixed_store_config(mkdtemp_clean(), {}, include_xml=False)
# Tell Django to clean out all databases, not just default
multi_db = True
@classmethod
def setUpClass(cls):
super(SharedModuleStoreTestCase, cls).setUpClass()
def _setUpModuleStore(cls): # pylint: disable=invalid-name
"""
Set up the modulestore for an entire test class.
"""
cls._settings_override = override_settings(MODULESTORE=cls.MODULESTORE)
cls._settings_override.__enter__()
XMODULE_FACTORY_LOCK.enable()
......@@ -277,6 +281,40 @@ class SharedModuleStoreTestCase(TestCase):
cls.store = modulestore()
@classmethod
@contextmanager
def setUpClassAndTestData(cls): # pylint: disable=invalid-name
"""
For use when the test class has a setUpTestData() method that uses variables
that are setup during setUpClass() of the same test class.
Use it like so:
@classmethod
def setUpClass(cls):
with super(MyTestClass, cls).setUpClassAndTestData():
<all the cls.setUpClass() setup code that performs modulestore setup...>
@classmethod
def setUpTestData(cls):
<all the setup code that creates Django models per test class...>
<these models can use variables (courses) setup in setUpClass() above>
"""
cls._setUpModuleStore()
# Now yield to allow the test class to run its setUpClass() setup code.
yield
# Now call the base class, which calls back into the test class's setUpTestData().
super(SharedModuleStoreTestCase, cls).setUpClass()
@classmethod
def setUpClass(cls):
"""
For use when the test class has no setUpTestData() method -or-
when that method does not use variable set up in setUpClass().
"""
super(SharedModuleStoreTestCase, cls).setUpClass()
cls._setUpModuleStore()
@classmethod
def tearDownClass(cls):
drop_mongo_collections() # pylint: disable=no-value-for-parameter
clear_all_caches()
......@@ -392,6 +430,8 @@ class ModuleStoreTestCase(TestCase):
"""
MODULESTORE = mixed_store_config(mkdtemp_clean(), {}, include_xml=False)
# Tell Django to clean out all databases, not just default
multi_db = True
def setUp(self, **kwargs):
"""
......
......@@ -24,8 +24,6 @@ from opaque_keys.edx.locator import CourseLocator, LibraryLocator
DRAFT_DIR = "drafts"
PUBLISHED_DIR = "published"
EXPORT_VERSION_FILE = "format.json"
EXPORT_VERSION_KEY = "export_format"
DEFAULT_CONTENT_FIELDS = ['metadata', 'data']
......@@ -408,90 +406,3 @@ def export_extra_content(export_fs, modulestore, source_course_key, dest_course_
# export content fields other then metadata and data in json format in current directory
_export_field_content(item, item_dir)
def convert_between_versions(source_dir, target_dir):
"""
Converts a version 0 export format to version 1, and vice versa.
@param source_dir: the directory structure with the course export that should be converted.
The contents of source_dir will not be altered.
@param target_dir: the directory where the converted export should be written.
@return: the version number of the converted export.
"""
def convert_to_version_1():
""" Convert a version 0 archive to version 0 """
os.mkdir(copy_root)
with open(copy_root / EXPORT_VERSION_FILE, 'w') as f:
f.write('{{"{export_key}": 1}}\n'.format(export_key=EXPORT_VERSION_KEY))
# If a drafts folder exists, copy it over.
copy_drafts()
# Now copy everything into the published directory
published_dir = copy_root / PUBLISHED_DIR
shutil.copytree(path(source_dir) / course_name, published_dir)
# And delete the nested drafts directory, if it exists.
nested_drafts_dir = published_dir / DRAFT_DIR
if nested_drafts_dir.isdir():
shutil.rmtree(nested_drafts_dir)
def convert_to_version_0():
""" Convert a version 1 archive to version 0 """
# Copy everything in "published" up to the top level.
published_dir = path(source_dir) / course_name / PUBLISHED_DIR
if not published_dir.isdir():
raise ValueError("a version 1 archive must contain a published branch")
shutil.copytree(published_dir, copy_root)
# If there is a DRAFT branch, copy it. All other branches are ignored.
copy_drafts()
def copy_drafts():
"""
Copy drafts directory from the old archive structure to the new.
"""
draft_dir = path(source_dir) / course_name / DRAFT_DIR
if draft_dir.isdir():
shutil.copytree(draft_dir, copy_root / DRAFT_DIR)
root = os.listdir(source_dir)
if len(root) != 1 or (path(source_dir) / root[0]).isfile():
raise ValueError("source archive does not have single course directory at top level")
course_name = root[0]
# For this version of the script, we simply convert back and forth between version 0 and 1.
original_version = get_version(path(source_dir) / course_name)
if original_version not in [0, 1]:
raise ValueError("unknown version: " + str(original_version))
desired_version = 1 if original_version is 0 else 0
copy_root = path(target_dir) / course_name
if desired_version == 1:
convert_to_version_1()
else:
convert_to_version_0()
return desired_version
def get_version(course_path):
"""
Return the export format version number for the given
archive directory structure (represented as a path instance).
If the archived file does not correspond to a known export
format, None will be returned.
"""
format_file = course_path / EXPORT_VERSION_FILE
if not format_file.isfile():
return 0
with open(format_file, "r") as f:
data = json.load(f)
if EXPORT_VERSION_KEY in data:
return data[EXPORT_VERSION_KEY]
return None
......@@ -607,6 +607,7 @@ class LibraryImportManager(ImportManager):
org=self.target_id.org,
library=self.target_id.library,
user_id=self.user_id,
fields={"display_name": ""},
)
runtime = library.runtime
except DuplicateCourseError:
......
......@@ -5,14 +5,14 @@ data: |
<p>To use this template, replace the example text with your own text.</p>
<p>When you add the component, be sure to select <strong>Settings</strong>
to specify a <strong>Display Name</strong> and other values that apply.</p>
<h3>Announcement Date</h3>
<h3 class="hd hd-2">Announcement Date</h3>
<section class='update-description'>
<section class='primary'>
<p>Short note that introduces the topic</p>
<p class='author'>Instructor's name</p>
</section>
<h4>Heading for announcement 1</h4>
<h4 class="hd hd-4">Heading for announcement 1</h4>
<p>Announcement 1 text</p>
<h4>Heading for announcement 2</h4>
<h4 class="hd hd-4">Heading for announcement 2</h4>
<p>Announcement 2 text</p>
</section>
\ No newline at end of file
</section>
......@@ -2,7 +2,7 @@
metadata:
display_name: IFrame Tool
data: |
<h3>IFrame Tool</h3>
<h3 class="hd hd-2">IFrame Tool</h3>
<p>Use the IFrame tool to embed an exercise or tool from any web site into your course content. For example, the tool below allows learners to experiment with how the shape of a triangle affects a line that is derived from the triangle.</p>
<p>Exercises in an IFrame are not graded. To embed graded exercises, use a Custom JavaScript Problem.</p>
<p>The following code is the HTML format required to use the IFrame tool. For the IFrame in this template, you must replace the values in <i>italics</i>.</p>
......
......@@ -2,7 +2,7 @@
metadata:
display_name: Full Screen Image Tool
data: |
<h3>Full Screen Image Tool</h3>
<h3 class="hd hd-2">Full Screen Image Tool</h3>
<p>Use the Full Screen Image tool to allow learners to open and zoom in on a larger version of an image in your course.</p>
<p>With the Full Screen Image tool, learners can see the image's details as well as its context within the unit.</p>
<p>To enable users to view the larger image, you wrap the smaller image in a link to the larger version of the image.</p>
......
......@@ -12,7 +12,7 @@ metadata:
data: |
<html>
<h3>Example: E-text page</h3>
<h3 class="hd hd-2">Example: E-text page</h3>
<p>You can write complex equations in LaTeX.</p>
<p>When you add the component, be sure to select <strong>Settings</strong>
to specify a <strong>Display Name</strong> and other values that apply.</p>
......
......@@ -2,7 +2,7 @@
metadata:
display_name: Zooming Image Tool
data: |
<h3>Zooming Image Tool</h3>
<h3 class="hd hd-2">Zooming Image Tool</h3>
<p>Use the Zooming Image Tool to enable learners to see details of large, complex images.</p>
<p>With the Zooming Image Tool, the learner can move the mouse pointer over a part of the image to enlarge it and see more detail.</p>
<p>To use the Zooming Image Tool, you must first add the <a href="http://files.edx.org/jquery.loupeAndLightbox.js" target="_blank">jquery.loupeAndLightbox.js JavaScript file</a> to your course.</p>
......
......@@ -25,9 +25,6 @@ from xblock.test.tools import blocks_are_equivalent
from opaque_keys.edx.locations import Location
from xmodule.modulestore import EdxJSONEncoder
from xmodule.modulestore.xml import XMLModuleStore
from xmodule.modulestore.xml_exporter import (
convert_between_versions, get_version
)
from xmodule.tests import DATA_DIR
from xmodule.tests.helpers import directories_equal
from xmodule.x_module import XModuleMixin
......@@ -214,173 +211,3 @@ class TestEdxJsonEncoder(unittest.TestCase):
with self.assertRaises(TypeError):
self.encoder.default({})
class ConvertExportFormat(unittest.TestCase):
"""
Tests converting between export formats.
"""
def setUp(self):
""" Common setup. """
super(ConvertExportFormat, self).setUp()
# Directory for expanding all the test archives
self.temp_dir = mkdtemp()
self.addCleanup(shutil.rmtree, self.temp_dir)
# Directory where new archive will be created
self.result_dir = path(self.temp_dir) / uuid.uuid4().hex
os.mkdir(self.result_dir)
# Expand all the test archives and store their paths.
self.data_dir = path(__file__).realpath().parent / 'data'
self._version0_nodrafts = None
self._version1_nodrafts = None
self._version0_drafts = None
self._version1_drafts = None
self._version1_drafts_extra_branch = None
self._no_version = None
@property
def version0_nodrafts(self):
"lazily expand this"
if self._version0_nodrafts is None:
self._version0_nodrafts = self._expand_archive('Version0_nodrafts.tar.gz')
return self._version0_nodrafts
@property
def version1_nodrafts(self):
"lazily expand this"
if self._version1_nodrafts is None:
self._version1_nodrafts = self._expand_archive('Version1_nodrafts.tar.gz')
return self._version1_nodrafts
@property
def version0_drafts(self):
"lazily expand this"
if self._version0_drafts is None:
self._version0_drafts = self._expand_archive('Version0_drafts.tar.gz')
return self._version0_drafts
@property
def version1_drafts(self):
"lazily expand this"
if self._version1_drafts is None:
self._version1_drafts = self._expand_archive('Version1_drafts.tar.gz')
return self._version1_drafts
@property
def version1_drafts_extra_branch(self):
"lazily expand this"
if self._version1_drafts_extra_branch is None:
self._version1_drafts_extra_branch = self._expand_archive('Version1_drafts_extra_branch.tar.gz')
return self._version1_drafts_extra_branch
@property
def no_version(self):
"lazily expand this"
if self._no_version is None:
self._no_version = self._expand_archive('NoVersionNumber.tar.gz')
return self._no_version
def _expand_archive(self, name):
""" Expand archive into a directory and return the directory. """
target = path(self.temp_dir) / uuid.uuid4().hex
os.mkdir(target)
with tarfile.open(self.data_dir / name) as tar_file:
tar_file.extractall(path=target)
return target
def test_no_version(self):
""" Test error condition of no version number specified. """
errstring = "unknown version"
with self.assertRaisesRegexp(ValueError, errstring):
convert_between_versions(self.no_version, self.result_dir)
def test_no_published(self):
""" Test error condition of a version 1 archive with no published branch. """
errstring = "version 1 archive must contain a published branch"
no_published = self._expand_archive('Version1_nopublished.tar.gz')
with self.assertRaisesRegexp(ValueError, errstring):
convert_between_versions(no_published, self.result_dir)
def test_empty_course(self):
""" Test error condition of a version 1 archive with no published branch. """
errstring = "source archive does not have single course directory at top level"
empty_course = self._expand_archive('EmptyCourse.tar.gz')
with self.assertRaisesRegexp(ValueError, errstring):
convert_between_versions(empty_course, self.result_dir)
def test_convert_to_1_nodrafts(self):
"""
Test for converting from version 0 of export format to version 1 in a course with no drafts.
"""
self._verify_conversion(self.version0_nodrafts, self.version1_nodrafts)
def test_convert_to_1_drafts(self):
"""
Test for converting from version 0 of export format to version 1 in a course with drafts.
"""
self._verify_conversion(self.version0_drafts, self.version1_drafts)
def test_convert_to_0_nodrafts(self):
"""
Test for converting from version 1 of export format to version 0 in a course with no drafts.
"""
self._verify_conversion(self.version1_nodrafts, self.version0_nodrafts)
def test_convert_to_0_drafts(self):
"""
Test for converting from version 1 of export format to version 0 in a course with drafts.
"""
self._verify_conversion(self.version1_drafts, self.version0_drafts)
def test_convert_to_0_extra_branch(self):
"""
Test for converting from version 1 of export format to version 0 in a course
with drafts and an extra branch.
"""
self._verify_conversion(self.version1_drafts_extra_branch, self.version0_drafts)
def test_equality_function(self):
"""
Check equality function returns False for unequal directories.
"""
self.assertFalse(directories_equal(self.version1_nodrafts, self.version0_nodrafts))
self.assertFalse(directories_equal(self.version1_drafts_extra_branch, self.version1_drafts))
def test_version_0(self):
"""
Check that get_version correctly identifies a version 0 archive (old format).
"""
self.assertEqual(0, self._version_test(self.version0_nodrafts))
def test_version_1(self):
"""
Check that get_version correctly identifies a version 1 archive (new format).
"""
self.assertEqual(1, self._version_test(self.version1_nodrafts))
def test_version_missing(self):
"""
Check that get_version returns None if no version number is specified,
and the archive is not version 0.
"""
self.assertIsNone(self._version_test(self.no_version))
def _version_test(self, archive_dir):
"""
Helper function for version tests.
"""
root = os.listdir(archive_dir)
course_directory = archive_dir / root[0]
return get_version(course_directory)
def _verify_conversion(self, source_archive, comparison_archive):
"""
Helper function for conversion tests.
"""
convert_between_versions(source_archive, self.result_dir)
self.assertTrue(directories_equal(self.result_dir, comparison_archive))
......@@ -20,45 +20,50 @@
scrollbar-track-color: #F5F5F5;
}
.mce-content-body h1 {
.mce-content-body h1,
.mce-content-body .hd-1 {
color: #3c3c3c;
font-weight: normal;
font-size: 2em;
line-height: 1.4em;
letter-spacing: 1px;
margin: 0 0 1.416em 0;
margin: 0 0 1.41575em 0;
}
.mce-content-body h2 {
.mce-content-body h2,
.mce-content-body .hd-2,
.mce-content-body h3 {
color: #646464;
font-weight: 300;
font-size: 1.2em;
line-height: 1.2em;
letter-spacing: 1px;
margin-bottom: 15px;
font-size: 1.6em;
line-height: 1.4em;
margin-bottom: 1.6em;
text-transform: uppercase;
-webkit-font-smoothing: antialiased;
}
.mce-content-body h3, .mce-content-body h4, .mce-content-body h5, .mce-content-body h6 {
.mce-content-body .hd-3,
.mce-content-body h4,
.mce-content-body .hd-4,
.mce-content-body h5,
.mce-content-body .hd-5,
.mce-content-body h6,
.mce-content-body .hd-6 {
margin: 0 0 10px 0;
font-weight: 600;
}
.mce-content-body h3 {
font-size: 1.2em;
}
.mce-content-body h4 {
font-size: 1em;
.mce-content-body h4,
.mce-content-body .hd-4 {
font-size: 1.4em;
}
.mce-content-body h5 {
font-size: .83em;
.mce-content-body h5,
.mce-content-body .hd-5 {
font-size: 1.2em;
}
.mce-content-body h6 {
font-size: 0.75em;
.mce-content-body h6,
.mce-content-body .hd-6 {
font-size: 1em;
}
.mce-content-body p {
......
......@@ -839,6 +839,7 @@ class SpecialExamsPageAllowanceSection(PageObject):
self.q(css='input#user_info').fill(username)
self.q(css="input#addNewAllowance").click()
self.wait_for_element_absence("div.modal div.modal-header", "Popup should be hidden")
self.wait_for_ajax()
class SpecialExamsPageAttemptsSection(PageObject):
......
......@@ -44,5 +44,5 @@ class LibraryContentXBlockWrapper(PageObject):
"""
Gets headers of all child XBlocks as list of strings
"""
child_blocks_headers = self.q(css=self._bounded_selector("div[data-id] h3.problem-header"))
child_blocks_headers = self.q(css=self._bounded_selector("div[data-id] .problem-header"))
return frozenset(child.text for child in child_blocks_headers)
......@@ -15,7 +15,7 @@ class DashboardPage(PageObject):
url = BASE_URL + "/course/"
def is_browser_on_page(self):
return self.q(css='body.view-dashboard').present
return self.q(css='.content-primary').visible
@property
def course_runs(self):
......
......@@ -290,6 +290,7 @@ class ProctoredExamsTest(BaseInstructorDashboardTest):
# Stop the timed exam.
self.courseware_page.stop_timed_exam()
@flaky # TODO: fix this. See SOL-1654, SOL-1183, and SOL-1182
def test_can_add_remove_allowance(self):
"""
Make sure that allowances can be added and removed.
......
......@@ -4,6 +4,8 @@ from common.test.acceptance.pages.lms.oauth2_confirmation import OAuth2Confirmat
from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage
from bok_choy.web_app_test import WebAppTest
from flaky import flaky
from urlparse import urlparse, parse_qsl
......@@ -42,10 +44,15 @@ class OAuth2PermissionDelegationTests(WebAppTest):
assert self.oauth_page.visit()
self.oauth_page.cancel()
# This redirects to an invalid URI.
query = self._qs(self.browser.current_url)
self.assertEqual('access_denied', query['error'])
# This redirects to an invalid URI. For chrome verify title, current_url otherwise
if self.browser.name == 'chrome':
query = self._qs(self.browser.title)
self.assertIn('access_denied', query['error'])
else:
query = self._qs(self.browser.current_url)
self.assertIn('access_denied', query['error'])
@flaky # TODO, fix this: TNL-4190
def test_accepting_redirects(self):
"""
If you accept the request, you're redirected to the redirect_url with
......@@ -53,8 +60,17 @@ class OAuth2PermissionDelegationTests(WebAppTest):
"""
self._auth()
assert self.oauth_page.visit()
self.oauth_page.confirm()
# This redirects to an invalid URI.
query = self._qs(self.browser.current_url)
self.oauth_page.confirm()
self.oauth_page.wait_for_element_absence('input[name=authorize]', 'Authorization button is not present')
# Due to a bug in ChromeDriver, when chrome is on invalid URI,self.browser.current_url outputs
# data:text/html,chromewebdata. When this happens in our case,query string is present in the title.
# So to get query string, we branch out based on selected browser.
if self.browser.name == 'chrome':
query = self._qs(self.browser.title)
else:
query = self._qs(self.browser.current_url)
self.assertIn('code', query)
......@@ -4,6 +4,8 @@ Acceptance tests for the Import and Export pages
from nose.plugins.attrib import attr
from datetime import datetime
from flaky import flaky
from abc import abstractmethod
from bok_choy.promise import EmptyPromise
......@@ -180,6 +182,7 @@ class ImportTestMixin(object):
"""
return []
@flaky # TODO, fix this: TNL-4191
def test_upload(self):
"""
Scenario: I want to upload a course or library for import.
......
......@@ -5,6 +5,7 @@ from ...pages.studio.asset_index import AssetIndexPage
from .base_studio_test import StudioCourseTest
from ...fixtures.base import StudioApiLoginError
from ..helpers import skip_if_browser
class AssetIndexTest(StudioCourseTest):
......@@ -12,7 +13,6 @@ class AssetIndexTest(StudioCourseTest):
"""
Tests for the Asset index page.
"""
def setUp(self, is_staff=False):
super(AssetIndexTest, self).setUp()
self.asset_page = AssetIndexPage(
......@@ -28,12 +28,7 @@ class AssetIndexTest(StudioCourseTest):
"""
self.course_fixture.add_asset(['image.jpg', 'textbook.pdf'])
def test_page_existence(self):
"""
Make sure that the page is accessible.
"""
self.asset_page.visit()
@skip_if_browser('chrome') # TODO Need to fix test_page_existance for this for chrome browser
def test_type_filter_exists(self):
"""
Make sure type filter is on the page.
......@@ -41,6 +36,7 @@ class AssetIndexTest(StudioCourseTest):
self.asset_page.visit()
assert self.asset_page.type_filter_on_page() is True
@skip_if_browser('chrome') # TODO Need to fix test_page_existance for this for chrome browser
def test_filter_results(self):
"""
Make sure type filter actually filters the results.
......
......@@ -49,7 +49,6 @@ class CreateLibraryTest(WebAppTest):
self.auth_page.visit()
self.dashboard_page.visit()
self.dashboard_page.wait_for_element_visibility('.content-primary', 'See library list.')
self.assertFalse(self.dashboard_page.has_library(name=name, org=org, number=number))
self.assertTrue(self.dashboard_page.has_new_library_button())
......
......@@ -10,6 +10,7 @@ from .base_studio_test import StudioCourseTest
from ...pages.lms.create_mode import ModeCreationPage
from ...pages.studio.settings_certificates import CertificatesPage
from ...pages.studio.settings_advanced import AdvancedSettingsPage
from ..helpers import skip_if_browser
@attr('shard_8')
......@@ -160,6 +161,7 @@ class CertificatesTest(StudioCourseTest):
self.certificates_page.visit()
self.assertEqual(len(self.certificates_page.certificates), 0)
@skip_if_browser('chrome') # TODO Need to fix this for chrome browser
def test_can_create_and_edit_signatories_of_certficate(self):
"""
Scenario: Ensure that the certificates can be created with signatories and edited correctly.
......
......@@ -115,7 +115,7 @@ class AnnotatableProblemTest(UniqueCourseTest):
self.courseware_page.visit()
annotation_component_page = AnnotationComponentPage(self.browser)
self.assertEqual(
annotation_component_page.component_name, 'TEST ANNOTATION MODULE'.format()
annotation_component_page.component_name, 'Test Annotation Module'.format()
)
return annotation_component_page
......
......@@ -630,10 +630,11 @@ class YouTubeVideoTest(VideoBaseTest):
"""
Scenario: Multiple videos in sequentials all load and work, switching between sequentials
Given it has videos "A,B" in "Youtube" mode in position "1" of sequential
And videos "E,F" in "Youtube" mode in position "2" of sequential
And videos "C,D" in "Youtube" mode in position "2" of sequential
"""
self.verticals = [
[{'display_name': 'A'}, {'display_name': 'B'}], [{'display_name': 'C'}, {'display_name': 'D'}]
[{'display_name': 'A'}, {'display_name': 'B'}],
[{'display_name': 'C'}, {'display_name': 'D'}]
]
tab1_video_names = ['A', 'B']
......@@ -651,15 +652,16 @@ class YouTubeVideoTest(VideoBaseTest):
# go to video
self.navigate_to_video()
execute_video_steps(tab1_video_names)
# go to second sequential position
# import ipdb; ipdb.set_trace()
self.go_to_sequential_position(2)
execute_video_steps(tab2_video_names)
# go back to first sequential position
# we are again playing tab 1 videos to ensure that switching didn't broke some video functionality.
# import ipdb; ipdb.set_trace()
self.go_to_sequential_position(1)
execute_video_steps(tab1_video_names)
......
<library xblock-family="xblock.v1" display_name="Test Problems" org="TestOrg" library="TestProbs100">
<problem url_name="afe9dbb29b724181944f56617c72b3e5"/>
<problem url_name="ba28f97e8f33414e9a5de0068508f7fa"/>
</library>
<problem display_name="Multiple Choice" markdown="Multiple choice problems allow learners to select only one option. Learners can see all the options along with the problem text.&#10;&#10;When you add the problem, be sure to select Settings to specify a Display Name and other values that apply.&#10;&#10;You can use the following example problem as a model.&#10;&#10;&gt;&gt;Which of the following countries has the largest population?&lt;&lt;&#10;( ) Brazil {{ timely feedback -- explain why an almost correct answer is wrong }}&#10;( ) Germany&#10;(x) Indonesia&#10;( ) Russia&#10;&#10;[explanation]&#10;According to September 2014 estimates:&#10;The population of Indonesia is approximately 250 million.&#10;The population of Brazil is approximately 200 million.&#10;The population of Russia is approximately 146 million.&#10;The population of Germany is approximately 81 million.&#10;[explanation]&#10;">
<p>Multiple choice problems allow learners to select only one option.
Learners can see all the options along with the problem text.</p>
<p>When you add the problem, be sure to select <strong>Settings</strong>
to specify a <strong>Display Name</strong> and other values that apply.</p>
<p>You can use the following example problem as a model.</p>
<p>Which of the following countries has the largest population?</p>
<multiplechoiceresponse>
<choicegroup type="MultipleChoice">
<choice correct="false" name="brazil">Brazil
<choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint>
</choice>
<choice correct="false" name="germany">Germany</choice>
<choice correct="true" name="indonesia">Indonesia</choice>
<choice correct="false" name="russia">Russia</choice>
</choicegroup>
</multiplechoiceresponse>
<solution>
<div class="detailed-solution">
<p>Explanation</p>
<p>According to September 2014 estimates:</p>
<p>The population of Indonesia is approximately 250 million.</p>
<p>The population of Brazil is approximately 200 million.</p>
<p>The population of Russia is approximately 146 million.</p>
<p>The population of Germany is approximately 81 million.</p>
</div>
</solution>
</problem>
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
-- MySQL dump 10.13 Distrib 5.6.24, for debian-linux-gnu (x86_64)
--
-- Host: localhost Database: edxtest
-- ------------------------------------------------------
-- Server version 5.6.24-2+deb.sury.org~precise+2
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Dumping data for table `django_migrations`
--
LOCK TABLES `django_migrations` WRITE;
/*!40000 ALTER TABLE `django_migrations` DISABLE KEYS */;
INSERT INTO `django_migrations` VALUES (1,'contenttypes','0001_initial','2016-01-22 20:04:03.218179'),(2,'auth','0001_initial','2016-01-22 20:04:03.554767'),(3,'admin','0001_initial','2016-01-22 20:04:03.665239'),(4,'assessment','0001_initial','2016-01-22 20:04:06.917971'),(5,'assessment','0002_staffworkflow','2016-01-22 20:04:07.125841'),(6,'contenttypes','0002_remove_content_type_name','2016-01-22 20:04:07.307570'),(7,'auth','0002_alter_permission_name_max_length','2016-01-22 20:04:07.383568'),(8,'auth','0003_alter_user_email_max_length','2016-01-22 20:04:07.464880'),(9,'auth','0004_alter_user_username_opts','2016-01-22 20:04:07.496608'),(10,'auth','0005_alter_user_last_login_null','2016-01-22 20:04:07.584818'),(11,'auth','0006_require_contenttypes_0002','2016-01-22 20:04:07.591565'),(12,'bookmarks','0001_initial','2016-01-22 20:04:07.932691'),(13,'branding','0001_initial','2016-01-22 20:04:08.105513'),(14,'bulk_email','0001_initial','2016-01-22 20:04:08.469986'),(15,'bulk_email','0002_data__load_course_email_template','2016-01-22 20:04:08.538284'),(16,'instructor_task','0001_initial','2016-01-22 20:04:08.743012'),(17,'certificates','0001_initial','2016-01-22 20:04:09.838773'),(18,'certificates','0002_data__certificatehtmlviewconfiguration_data','2016-01-22 20:04:09.865157'),(19,'certificates','0003_data__default_modes','2016-01-22 20:04:09.943086'),(20,'certificates','0004_certificategenerationhistory','2016-01-22 20:04:10.116067'),(21,'certificates','0005_auto_20151208_0801','2016-01-22 20:04:10.232215'),(22,'certificates','0006_certificatetemplateasset_asset_slug','2016-01-22 20:04:10.307271'),(23,'certificates','0007_certificateinvalidation','2016-01-22 20:04:10.510089'),(24,'commerce','0001_data__add_ecommerce_service_user','2016-01-22 20:04:10.538558'),(25,'cors_csrf','0001_initial','2016-01-22 20:04:10.680353'),(26,'course_action_state','0001_initial','2016-01-22 20:04:11.030484'),(27,'course_groups','0001_initial','2016-01-22 20:04:12.283946'),(28,'course_modes','0001_initial','2016-01-22 20:04:12.452546'),(29,'course_modes','0002_coursemode_expiration_datetime_is_explicit','2016-01-22 20:04:12.534953'),(30,'course_modes','0003_auto_20151113_1443','2016-01-22 20:04:12.569936'),(31,'course_modes','0004_auto_20151113_1457','2016-01-22 20:04:12.776505'),(32,'course_modes','0005_auto_20151217_0958','2016-01-22 20:04:12.807317'),(33,'course_overviews','0001_initial','2016-01-22 20:04:12.927012'),(34,'course_overviews','0002_add_course_catalog_fields','2016-01-22 20:04:13.220589'),(35,'course_overviews','0003_courseoverviewgeneratedhistory','2016-01-22 20:04:13.258023'),(36,'course_overviews','0004_courseoverview_org','2016-01-22 20:04:13.346700'),(37,'course_overviews','0005_delete_courseoverviewgeneratedhistory','2016-01-22 20:04:13.375580'),(38,'course_overviews','0006_courseoverviewimageset','2016-01-22 20:04:13.464516'),(39,'course_overviews','0007_courseoverviewimageconfig','2016-01-22 20:04:13.651285'),(40,'course_structures','0001_initial','2016-01-22 20:04:13.689179'),(41,'courseware','0001_initial','2016-01-22 20:04:17.245019'),(42,'credentials','0001_initial','2016-01-22 20:04:17.454975'),(43,'credentials','0002_data__add_service_user','2016-01-22 20:04:17.494478'),(44,'credit','0001_initial','2016-01-22 20:04:19.593607'),(45,'dark_lang','0001_initial','2016-01-22 20:04:19.851812'),(46,'dark_lang','0002_data__enable_on_install','2016-01-22 20:04:19.886816'),(47,'default','0001_initial','2016-01-22 20:04:20.656170'),(48,'default','0002_add_related_name','2016-01-22 20:04:20.920330'),(49,'default','0003_alter_email_max_length','2016-01-22 20:04:21.038154'),(50,'django_comment_common','0001_initial','2016-01-22 20:04:21.777797'),(51,'django_notify','0001_initial','2016-01-22 20:04:23.074494'),(52,'django_openid_auth','0001_initial','2016-01-22 20:04:23.435834'),(53,'edx_proctoring','0001_initial','2016-01-22 20:04:28.470618'),(54,'edx_proctoring','0002_proctoredexamstudentattempt_is_status_acknowledged','2016-01-22 20:04:28.834557'),(55,'edx_proctoring','0003_auto_20160101_0525','2016-01-22 20:04:29.402026'),(56,'edxval','0001_initial','2016-01-22 20:04:30.249661'),(57,'edxval','0002_data__default_profiles','2016-01-22 20:04:30.307115'),(58,'embargo','0001_initial','2016-01-22 20:04:31.462353'),(59,'embargo','0002_data__add_countries','2016-01-22 20:04:32.017366'),(60,'external_auth','0001_initial','2016-01-22 20:04:32.753375'),(61,'lms_xblock','0001_initial','2016-01-22 20:04:33.090177'),(62,'sites','0001_initial','2016-01-22 20:04:33.156273'),(63,'microsite_configuration','0001_initial','2016-01-22 20:04:35.468782'),(64,'milestones','0001_initial','2016-01-22 20:04:36.698849'),(65,'milestones','0002_data__seed_relationship_types','2016-01-22 20:04:36.760089'),(66,'mobile_api','0001_initial','2016-01-22 20:04:38.189431'),(67,'notes','0001_initial','2016-01-22 20:04:38.639288'),(68,'oauth2','0001_initial','2016-01-22 20:04:40.968535'),(69,'oauth2_provider','0001_initial','2016-01-22 20:04:41.475836'),(70,'oauth_provider','0001_initial','2016-01-22 20:04:42.704759'),(71,'organizations','0001_initial','2016-01-22 20:04:42.985226'),(72,'organizations','0002_auto_20151119_2048','2016-01-22 20:04:43.049898'),(73,'problem_builder','0001_initial','2016-01-22 20:04:43.244682'),(74,'programs','0001_initial','2016-01-22 20:04:43.776265'),(75,'programs','0002_programsapiconfig_cache_ttl','2016-01-22 20:04:44.329122'),(76,'programs','0003_auto_20151120_1613','2016-01-22 20:04:46.494468'),(77,'rss_proxy','0001_initial','2016-01-22 20:04:46.572259'),(78,'self_paced','0001_initial','2016-01-22 20:04:47.142336'),(79,'sessions','0001_initial','2016-01-22 20:04:47.246566'),(80,'student','0001_initial','2016-01-22 20:05:04.804775'),(81,'shoppingcart','0001_initial','2016-01-22 20:05:20.059855'),(82,'shoppingcart','0002_auto_20151208_1034','2016-01-22 20:05:21.457216'),(83,'shoppingcart','0003_auto_20151217_0958','2016-01-22 20:05:22.922754'),(84,'splash','0001_initial','2016-01-22 20:05:23.712228'),(85,'static_replace','0001_initial','2016-01-22 20:05:24.489567'),(86,'status','0001_initial','2016-01-22 20:05:26.131108'),(87,'student','0002_auto_20151208_1034','2016-01-22 20:05:27.780831'),(88,'submissions','0001_initial','2016-01-22 20:05:29.846153'),(89,'submissions','0002_auto_20151119_0913','2016-01-22 20:05:30.098711'),(90,'survey','0001_initial','2016-01-22 20:05:31.137171'),(91,'teams','0001_initial','2016-01-22 20:05:33.436431'),(92,'third_party_auth','0001_initial','2016-01-22 20:05:38.010830'),(93,'track','0001_initial','2016-01-22 20:05:38.097314'),(94,'user_api','0001_initial','2016-01-22 20:05:44.556502'),(95,'util','0001_initial','2016-01-22 20:05:45.512595'),(96,'util','0002_data__default_rate_limit_config','2016-01-22 20:05:45.583331'),(97,'verify_student','0001_initial','2016-01-22 20:05:56.456343'),(98,'verify_student','0002_auto_20151124_1024','2016-01-22 20:05:58.780629'),(99,'verify_student','0003_auto_20151113_1443','2016-01-22 20:05:59.622634'),(100,'wiki','0001_initial','2016-01-22 20:06:30.273480'),(101,'wiki','0002_remove_article_subscription','2016-01-22 20:06:30.358472'),(102,'workflow','0001_initial','2016-01-22 20:06:30.741041'),(103,'xblock_django','0001_initial','2016-01-22 20:06:31.645852'),(104,'contentstore','0001_initial','2016-01-22 20:07:00.042399'),(105,'course_creators','0001_initial','2016-01-22 20:07:00.153769'),(106,'xblock_config','0001_initial','2016-01-22 20:07:00.552942');
/*!40000 ALTER TABLE `django_migrations` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2016-01-22 20:07:05
-- MySQL dump 10.13 Distrib 5.6.14, for debian-linux-gnu (x86_64)
--
-- Host: localhost Database: edxtest
-- ------------------------------------------------------
-- Server version 5.6.14-1+debphp.org~precise+1
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Dumping data for table `django_migrations`
--
LOCK TABLES `django_migrations` WRITE;
/*!40000 ALTER TABLE `django_migrations` DISABLE KEYS */;
INSERT INTO `django_migrations` VALUES (1,'contenttypes','0001_initial','2016-02-29 17:58:04.910265'),(2,'auth','0001_initial','2016-02-29 17:58:05.155297'),(3,'admin','0001_initial','2016-02-29 17:58:05.261098'),(4,'assessment','0001_initial','2016-02-29 17:58:08.458980'),(5,'assessment','0002_staffworkflow','2016-02-29 17:58:08.659081'),(6,'contenttypes','0002_remove_content_type_name','2016-02-29 17:58:08.818265'),(7,'auth','0002_alter_permission_name_max_length','2016-02-29 17:58:08.855835'),(8,'auth','0003_alter_user_email_max_length','2016-02-29 17:58:08.894682'),(9,'auth','0004_alter_user_username_opts','2016-02-29 17:58:08.918567'),(10,'auth','0005_alter_user_last_login_null','2016-02-29 17:58:08.971615'),(11,'auth','0006_require_contenttypes_0002','2016-02-29 17:58:08.976069'),(12,'bookmarks','0001_initial','2016-02-29 17:58:09.243246'),(13,'branding','0001_initial','2016-02-29 17:58:09.364920'),(14,'bulk_email','0001_initial','2016-02-29 17:58:09.630882'),(15,'bulk_email','0002_data__load_course_email_template','2016-02-29 17:58:09.715512'),(16,'instructor_task','0001_initial','2016-02-29 17:58:09.872596'),(17,'certificates','0001_initial','2016-02-29 17:58:10.722743'),(18,'certificates','0002_data__certificatehtmlviewconfiguration_data','2016-02-29 17:58:10.736641'),(19,'certificates','0003_data__default_modes','2016-02-29 17:58:10.775334'),(20,'certificates','0004_certificategenerationhistory','2016-02-29 17:58:10.911498'),(21,'certificates','0005_auto_20151208_0801','2016-02-29 17:58:11.002303'),(22,'certificates','0006_certificatetemplateasset_asset_slug','2016-02-29 17:58:11.050549'),(23,'certificates','0007_certificateinvalidation','2016-02-29 17:58:11.166297'),(24,'commerce','0001_data__add_ecommerce_service_user','2016-02-29 17:58:11.198023'),(25,'commerce','0002_commerceconfiguration','2016-02-29 17:58:11.291907'),(26,'contentserver','0001_initial','2016-02-29 17:58:11.375676'),(27,'cors_csrf','0001_initial','2016-02-29 17:58:11.462189'),(28,'course_action_state','0001_initial','2016-02-29 17:58:11.783744'),(29,'course_groups','0001_initial','2016-02-29 17:58:12.790006'),(30,'course_modes','0001_initial','2016-02-29 17:58:12.902796'),(31,'course_modes','0002_coursemode_expiration_datetime_is_explicit','2016-02-29 17:58:12.957209'),(32,'course_modes','0003_auto_20151113_1443','2016-02-29 17:58:12.979234'),(33,'course_modes','0004_auto_20151113_1457','2016-02-29 17:58:13.095320'),(34,'course_modes','0005_auto_20151217_0958','2016-02-29 17:58:13.116644'),(35,'course_modes','0006_auto_20160208_1407','2016-02-29 17:58:13.208193'),(36,'course_overviews','0001_initial','2016-02-29 17:58:13.283088'),(37,'course_overviews','0002_add_course_catalog_fields','2016-02-29 17:58:13.499090'),(38,'course_overviews','0003_courseoverviewgeneratedhistory','2016-02-29 17:58:13.525866'),(39,'course_overviews','0004_courseoverview_org','2016-02-29 17:58:13.567577'),(40,'course_overviews','0005_delete_courseoverviewgeneratedhistory','2016-02-29 17:58:13.585458'),(41,'course_overviews','0006_courseoverviewimageset','2016-02-29 17:58:13.636894'),(42,'course_overviews','0007_courseoverviewimageconfig','2016-02-29 17:58:13.764616'),(43,'course_overviews','0008_remove_courseoverview_facebook_url','2016-02-29 17:58:13.815867'),(44,'course_overviews','0009_readd_facebook_url','2016-02-29 17:58:13.872971'),(45,'course_structures','0001_initial','2016-02-29 17:58:13.899706'),(46,'courseware','0001_initial','2016-02-29 17:58:16.765503'),(47,'coursewarehistoryextended','0001_initial','2016-02-29 17:58:16.912454'),(48,'credentials','0001_initial','2016-02-29 17:58:17.072454'),(49,'credit','0001_initial','2016-02-29 17:58:18.478297'),(50,'dark_lang','0001_initial','2016-02-29 17:58:18.635638'),(51,'dark_lang','0002_data__enable_on_install','2016-02-29 17:58:18.654315'),(52,'default','0001_initial','2016-02-29 17:58:19.121037'),(53,'default','0002_add_related_name','2016-02-29 17:58:19.287646'),(54,'default','0003_alter_email_max_length','2016-02-29 17:58:19.355367'),(55,'django_comment_common','0001_initial','2016-02-29 17:58:19.826406'),(56,'django_notify','0001_initial','2016-02-29 17:58:20.691115'),(57,'django_openid_auth','0001_initial','2016-02-29 17:58:20.948196'),(58,'edx_proctoring','0001_initial','2016-02-29 17:58:25.028310'),(59,'edx_proctoring','0002_proctoredexamstudentattempt_is_status_acknowledged','2016-02-29 17:58:25.270722'),(60,'edx_proctoring','0003_auto_20160101_0525','2016-02-29 17:58:25.651659'),(61,'edx_proctoring','0004_auto_20160201_0523','2016-02-29 17:58:25.881209'),(62,'edxval','0001_initial','2016-02-29 17:58:26.490038'),(63,'edxval','0002_data__default_profiles','2016-02-29 17:58:26.519947'),(64,'embargo','0001_initial','2016-02-29 17:58:27.388295'),(65,'embargo','0002_data__add_countries','2016-02-29 17:58:27.716022'),(66,'external_auth','0001_initial','2016-02-29 17:58:28.339793'),(67,'lms_xblock','0001_initial','2016-02-29 17:58:28.600358'),(68,'sites','0001_initial','2016-02-29 17:58:28.639305'),(69,'microsite_configuration','0001_initial','2016-02-29 17:58:30.470563'),(70,'microsite_configuration','0002_auto_20160202_0228','2016-02-29 17:58:31.089884'),(71,'milestones','0001_initial','2016-02-29 17:58:32.206321'),(72,'milestones','0002_data__seed_relationship_types','2016-02-29 17:58:32.242905'),(73,'milestones','0003_coursecontentmilestone_requirements','2016-02-29 17:58:33.317573'),(74,'milestones','0004_auto_20151221_1445','2016-02-29 17:58:33.593210'),(75,'mobile_api','0001_initial','2016-02-29 17:58:33.849089'),(76,'notes','0001_initial','2016-02-29 17:58:34.185220'),(77,'oauth2','0001_initial','2016-02-29 17:58:35.892898'),(78,'oauth2_provider','0001_initial','2016-02-29 17:58:36.227236'),(79,'oauth_provider','0001_initial','2016-02-29 17:58:37.052991'),(80,'organizations','0001_initial','2016-02-29 17:58:37.290331'),(81,'programs','0001_initial','2016-02-29 17:58:37.667708'),(82,'programs','0002_programsapiconfig_cache_ttl','2016-02-29 17:58:38.067119'),(83,'programs','0003_auto_20151120_1613','2016-02-29 17:58:39.752602'),(84,'programs','0004_programsapiconfig_enable_certification','2016-02-29 17:58:40.179508'),(85,'programs','0005_programsapiconfig_max_retries','2016-02-29 17:58:40.596412'),(86,'rss_proxy','0001_initial','2016-02-29 17:58:40.641620'),(87,'self_paced','0001_initial','2016-02-29 17:58:41.078194'),(88,'sessions','0001_initial','2016-02-29 17:58:41.133704'),(89,'student','0001_initial','2016-02-29 17:58:53.980867'),(90,'shoppingcart','0001_initial','2016-02-29 17:59:06.233175'),(91,'shoppingcart','0002_auto_20151208_1034','2016-02-29 17:59:07.059000'),(92,'shoppingcart','0003_auto_20151217_0958','2016-02-29 17:59:07.907660'),(93,'splash','0001_initial','2016-02-29 17:59:08.351083'),(94,'static_replace','0001_initial','2016-02-29 17:59:08.818049'),(95,'static_replace','0002_assetexcludedextensionsconfig','2016-02-29 17:59:09.319193'),(96,'status','0001_initial','2016-02-29 17:59:10.500113'),(97,'student','0002_auto_20151208_1034','2016-02-29 17:59:11.650009'),(98,'submissions','0001_initial','2016-02-29 17:59:12.491480'),(99,'submissions','0002_auto_20151119_0913','2016-02-29 17:59:12.683289'),(100,'submissions','0003_submission_status','2016-02-29 17:59:12.811034'),(101,'survey','0001_initial','2016-02-29 17:59:13.767659'),(102,'teams','0001_initial','2016-02-29 17:59:16.603866'),(103,'third_party_auth','0001_initial','2016-02-29 17:59:19.924001'),(104,'track','0001_initial','2016-02-29 17:59:19.974203'),(105,'user_api','0001_initial','2016-02-29 17:59:24.197607'),(106,'util','0001_initial','2016-02-29 17:59:24.978364'),(107,'util','0002_data__default_rate_limit_config','2016-02-29 17:59:25.017205'),(108,'verify_student','0001_initial','2016-02-29 17:59:33.329519'),(109,'verify_student','0002_auto_20151124_1024','2016-02-29 17:59:34.238392'),(110,'verify_student','0003_auto_20151113_1443','2016-02-29 17:59:35.001393'),(111,'wiki','0001_initial','2016-02-29 17:59:56.418517'),(112,'wiki','0002_remove_article_subscription','2016-02-29 17:59:56.476734'),(113,'workflow','0001_initial','2016-02-29 17:59:56.760505'),(114,'xblock_django','0001_initial','2016-02-29 17:59:57.572216'),(115,'xblock_django','0002_auto_20160204_0809','2016-02-29 17:59:58.380438'),(116,'contentstore','0001_initial','2016-02-29 18:00:19.534458'),(117,'course_creators','0001_initial','2016-02-29 18:00:19.597219'),(118,'xblock_config','0001_initial','2016-02-29 18:00:19.855097');
/*!40000 ALTER TABLE `django_migrations` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2016-02-29 18:00:23
-- MySQL dump 10.13 Distrib 5.6.14, for debian-linux-gnu (x86_64)
--
-- Host: localhost Database: student_module_history_test
-- ------------------------------------------------------
-- Server version 5.6.14-1+debphp.org~precise+1
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Dumping data for table `django_migrations`
--
LOCK TABLES `django_migrations` WRITE;
/*!40000 ALTER TABLE `django_migrations` DISABLE KEYS */;
INSERT INTO `django_migrations` VALUES (1,'contenttypes','0001_initial','2016-02-29 18:01:39.511327'),(2,'auth','0001_initial','2016-02-29 18:01:39.547129'),(3,'admin','0001_initial','2016-02-29 18:01:39.568351'),(4,'assessment','0001_initial','2016-02-29 18:01:40.149754'),(5,'assessment','0002_staffworkflow','2016-02-29 18:01:40.160082'),(6,'contenttypes','0002_remove_content_type_name','2016-02-29 18:01:40.231907'),(7,'auth','0002_alter_permission_name_max_length','2016-02-29 18:01:40.251926'),(8,'auth','0003_alter_user_email_max_length','2016-02-29 18:01:40.276419'),(9,'auth','0004_alter_user_username_opts','2016-02-29 18:01:40.297224'),(10,'auth','0005_alter_user_last_login_null','2016-02-29 18:01:40.317098'),(11,'auth','0006_require_contenttypes_0002','2016-02-29 18:01:40.319374'),(12,'bookmarks','0001_initial','2016-02-29 18:01:40.396366'),(13,'branding','0001_initial','2016-02-29 18:01:40.453783'),(14,'bulk_email','0001_initial','2016-02-29 18:01:40.565081'),(15,'bulk_email','0002_data__load_course_email_template','2016-02-29 18:01:40.574000'),(16,'instructor_task','0001_initial','2016-02-29 18:01:40.613011'),(17,'certificates','0001_initial','2016-02-29 18:01:40.980762'),(18,'certificates','0002_data__certificatehtmlviewconfiguration_data','2016-02-29 18:01:40.991190'),(19,'certificates','0003_data__default_modes','2016-02-29 18:01:41.003742'),(20,'certificates','0004_certificategenerationhistory','2016-02-29 18:01:41.061308'),(21,'certificates','0005_auto_20151208_0801','2016-02-29 18:01:41.113837'),(22,'certificates','0006_certificatetemplateasset_asset_slug','2016-02-29 18:01:41.131141'),(23,'certificates','0007_certificateinvalidation','2016-02-29 18:01:41.189247'),(24,'commerce','0001_data__add_ecommerce_service_user','2016-02-29 18:01:41.200385'),(25,'commerce','0002_commerceconfiguration','2016-02-29 18:01:41.260287'),(26,'contentserver','0001_initial','2016-02-29 18:01:41.323580'),(27,'cors_csrf','0001_initial','2016-02-29 18:01:41.388714'),(28,'course_action_state','0001_initial','2016-02-29 18:01:41.514544'),(29,'course_groups','0001_initial','2016-02-29 18:01:42.036647'),(30,'course_modes','0001_initial','2016-02-29 18:01:42.068946'),(31,'course_modes','0002_coursemode_expiration_datetime_is_explicit','2016-02-29 18:01:42.084746'),(32,'course_modes','0003_auto_20151113_1443','2016-02-29 18:01:42.100516'),(33,'course_modes','0004_auto_20151113_1457','2016-02-29 18:01:42.187139'),(34,'course_modes','0005_auto_20151217_0958','2016-02-29 18:01:42.205556'),(35,'course_modes','0006_auto_20160208_1407','2016-02-29 18:01:42.289731'),(36,'course_overviews','0001_initial','2016-02-29 18:01:42.319879'),(37,'course_overviews','0002_add_course_catalog_fields','2016-02-29 18:01:42.402948'),(38,'course_overviews','0003_courseoverviewgeneratedhistory','2016-02-29 18:01:42.417563'),(39,'course_overviews','0004_courseoverview_org','2016-02-29 18:01:42.435373'),(40,'course_overviews','0005_delete_courseoverviewgeneratedhistory','2016-02-29 18:01:42.448847'),(41,'course_overviews','0006_courseoverviewimageset','2016-02-29 18:01:42.472337'),(42,'course_overviews','0007_courseoverviewimageconfig','2016-02-29 18:01:42.561497'),(43,'course_overviews','0008_remove_courseoverview_facebook_url','2016-02-29 18:01:42.582078'),(44,'course_overviews','0009_readd_facebook_url','2016-02-29 18:01:42.608691'),(45,'course_structures','0001_initial','2016-02-29 18:01:42.625296'),(46,'courseware','0001_initial','2016-02-29 18:01:44.499738'),(47,'coursewarehistoryextended','0001_initial','2016-02-29 18:01:44.691094'),(48,'credentials','0001_initial','2016-02-29 18:01:44.788912'),(49,'credit','0001_initial','2016-02-29 18:01:45.621214'),(50,'dark_lang','0001_initial','2016-02-29 18:01:45.752882'),(51,'dark_lang','0002_data__enable_on_install','2016-02-29 18:01:45.765638'),(52,'default','0001_initial','2016-02-29 18:01:46.107319'),(53,'default','0002_add_related_name','2016-02-29 18:01:46.247368'),(54,'default','0003_alter_email_max_length','2016-02-29 18:01:46.265241'),(55,'django_comment_common','0001_initial','2016-02-29 18:01:46.569556'),(56,'django_notify','0001_initial','2016-02-29 18:01:47.200935'),(57,'django_openid_auth','0001_initial','2016-02-29 18:01:47.399851'),(58,'edx_proctoring','0001_initial','2016-02-29 18:01:50.659784'),(59,'edx_proctoring','0002_proctoredexamstudentattempt_is_status_acknowledged','2016-02-29 18:01:50.845795'),(60,'edx_proctoring','0003_auto_20160101_0525','2016-02-29 18:01:51.213992'),(61,'edx_proctoring','0004_auto_20160201_0523','2016-02-29 18:01:51.397904'),(62,'edxval','0001_initial','2016-02-29 18:01:51.631665'),(63,'edxval','0002_data__default_profiles','2016-02-29 18:01:51.651781'),(64,'embargo','0001_initial','2016-02-29 18:01:52.230500'),(65,'embargo','0002_data__add_countries','2016-02-29 18:01:52.401900'),(66,'external_auth','0001_initial','2016-02-29 18:01:52.851531'),(67,'lms_xblock','0001_initial','2016-02-29 18:01:53.081369'),(68,'sites','0001_initial','2016-02-29 18:01:53.104608'),(69,'microsite_configuration','0001_initial','2016-02-29 18:01:54.497782'),(70,'microsite_configuration','0002_auto_20160202_0228','2016-02-29 18:01:55.048275'),(71,'milestones','0001_initial','2016-02-29 18:01:55.459781'),(72,'milestones','0002_data__seed_relationship_types','2016-02-29 18:01:55.481439'),(73,'milestones','0003_coursecontentmilestone_requirements','2016-02-29 18:01:55.522487'),(74,'milestones','0004_auto_20151221_1445','2016-02-29 18:01:55.688330'),(75,'mobile_api','0001_initial','2016-02-29 18:01:56.844055'),(76,'notes','0001_initial','2016-02-29 18:01:57.066857'),(77,'oauth2','0001_initial','2016-02-29 18:01:58.412080'),(78,'oauth2_provider','0001_initial','2016-02-29 18:01:58.699157'),(79,'oauth_provider','0001_initial','2016-02-29 18:01:59.448662'),(80,'organizations','0001_initial','2016-02-29 18:01:59.538595'),(81,'programs','0001_initial','2016-02-29 18:01:59.922304'),(82,'programs','0002_programsapiconfig_cache_ttl','2016-02-29 18:02:00.317781'),(83,'programs','0003_auto_20151120_1613','2016-02-29 18:02:01.977886'),(84,'programs','0004_programsapiconfig_enable_certification','2016-02-29 18:02:02.413721'),(85,'programs','0005_programsapiconfig_max_retries','2016-02-29 18:02:02.799700'),(86,'rss_proxy','0001_initial','2016-02-29 18:02:02.828648'),(87,'self_paced','0001_initial','2016-02-29 18:02:03.260889'),(88,'sessions','0001_initial','2016-02-29 18:02:03.285722'),(89,'student','0001_initial','2016-02-29 18:02:14.964735'),(90,'shoppingcart','0001_initial','2016-02-29 18:02:26.167709'),(91,'shoppingcart','0002_auto_20151208_1034','2016-02-29 18:02:26.959607'),(92,'shoppingcart','0003_auto_20151217_0958','2016-02-29 18:02:27.772373'),(93,'splash','0001_initial','2016-02-29 18:02:28.180056'),(94,'static_replace','0001_initial','2016-02-29 18:02:28.600901'),(95,'static_replace','0002_assetexcludedextensionsconfig','2016-02-29 18:02:29.072670'),(96,'status','0001_initial','2016-02-29 18:02:30.149381'),(97,'student','0002_auto_20151208_1034','2016-02-29 18:02:31.383085'),(98,'submissions','0001_initial','2016-02-29 18:02:31.704655'),(99,'submissions','0002_auto_20151119_0913','2016-02-29 18:02:31.818741'),(100,'submissions','0003_submission_status','2016-02-29 18:02:31.875924'),(101,'survey','0001_initial','2016-02-29 18:02:32.613867'),(102,'teams','0001_initial','2016-02-29 18:02:35.208964'),(103,'third_party_auth','0001_initial','2016-02-29 18:02:38.385039'),(104,'track','0001_initial','2016-02-29 18:02:38.420093'),(105,'user_api','0001_initial','2016-02-29 18:02:42.631297'),(106,'util','0001_initial','2016-02-29 18:02:43.444988'),(107,'util','0002_data__default_rate_limit_config','2016-02-29 18:02:43.472520'),(108,'verify_student','0001_initial','2016-02-29 18:02:51.517974'),(109,'verify_student','0002_auto_20151124_1024','2016-02-29 18:02:52.305832'),(110,'verify_student','0003_auto_20151113_1443','2016-02-29 18:02:53.133517'),(111,'wiki','0001_initial','2016-02-29 18:03:14.972792'),(112,'wiki','0002_remove_article_subscription','2016-02-29 18:03:15.007100'),(113,'workflow','0001_initial','2016-02-29 18:03:15.147297'),(114,'xblock_django','0001_initial','2016-02-29 18:03:15.981916'),(115,'xblock_django','0002_auto_20160204_0809','2016-02-29 18:03:16.844433'),(116,'contentstore','0001_initial','2016-02-29 18:03:37.119213'),(117,'course_creators','0001_initial','2016-02-29 18:03:37.149132'),(118,'xblock_config','0001_initial','2016-02-29 18:03:37.381904');
/*!40000 ALTER TABLE `django_migrations` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2016-02-29 18:03:40
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
DROP TABLE IF EXISTS `coursewarehistoryextended_studentmodulehistoryextended`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `coursewarehistoryextended_studentmodulehistoryextended` (
`version` varchar(255) DEFAULT NULL,
`created` datetime(6) NOT NULL,
`state` longtext,
`grade` double DEFAULT NULL,
`max_grade` double DEFAULT NULL,
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`student_module_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `coursewarehistoryextended_studentmodulehistoryextended_2af72f10` (`version`),
KEY `coursewarehistoryextended_studentmodulehistoryextended_e2fa5388` (`created`)
) ENGINE=InnoDB AUTO_INCREMENT=10000 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `django_migrations`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `django_migrations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`app` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`applied` datetime(6) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=119 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
......@@ -94,7 +94,7 @@ msgid ""
msgstr ""
"Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
"POT-Creation-Date: 2016-02-17 21:29+0000\n"
"POT-Creation-Date: 2016-02-24 18:01+0000\n"
"PO-Revision-Date: 2016-02-12 08:07+0000\n"
"Last-Translator: Soha Assali <soha+transifex@qordoba.com>\n"
"Language-Team: Arabic (http://www.transifex.com/open-edx/edx-platform/language/ar/)\n"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment