merging from master
2
.gitignore
vendored
@@ -41,6 +41,8 @@ conf/locale/en/LC_MESSAGES/*.po
|
||||
conf/locale/en/LC_MESSAGES/*.mo
|
||||
conf/locale/fake*/LC_MESSAGES/*.po
|
||||
conf/locale/fake*/LC_MESSAGES/*.mo
|
||||
conf/locale/eo/LC_MESSAGES/*.po
|
||||
conf/locale/eo/LC_MESSAGES/*.mo
|
||||
conf/locale/messages.mo
|
||||
|
||||
### Testing artifacts
|
||||
|
||||
2
AUTHORS
@@ -179,3 +179,5 @@ Henry Tareque <henry.tareque@gmail.com>
|
||||
Eugeny Kolpakov <eugeny.kolpakov@gmail.com>
|
||||
Omar Al-Ithawi <oithawi@qrf.org>
|
||||
Louis Pilfold <louis@lpil.uk>
|
||||
Akiva Leffert <akiva@edx.org>
|
||||
Mike Bifulco <mbifulco@aquent.com>
|
||||
@@ -7,6 +7,8 @@ the top. Include a label indicating the component affected.
|
||||
|
||||
Common: Add configurable reset button to units
|
||||
|
||||
Studio: Add support xblock validation messages on Studio unit/container page. TNL-683
|
||||
|
||||
LMS: Support adding cohorts from the instructor dashboard. TNL-162
|
||||
|
||||
LMS: Support adding students to a cohort via the instructor dashboard. TNL-163
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
|
||||
@@ -286,8 +286,15 @@ def _do_studio_prompt_action(intent, action):
|
||||
Wait for a studio prompt to appear and press the specified action button
|
||||
See cms/static/js/views/feedback_prompt.js for implementation
|
||||
"""
|
||||
assert intent in ['warning', 'error', 'confirmation', 'announcement',
|
||||
'step-required', 'help', 'mini']
|
||||
assert intent in [
|
||||
'warning',
|
||||
'error',
|
||||
'confirmation',
|
||||
'announcement',
|
||||
'step-required',
|
||||
'help',
|
||||
'mini',
|
||||
]
|
||||
assert action in ['primary', 'secondary']
|
||||
|
||||
world.wait_for_present('div.wrapper-prompt.is-shown#prompt-{}'.format(intent))
|
||||
@@ -333,7 +340,6 @@ def get_codemirror_value(index=0, find_prefix="$"):
|
||||
)
|
||||
|
||||
|
||||
|
||||
def attach_file(filename, sub_path):
|
||||
path = os.path.join(TEST_ROOT, sub_path, filename)
|
||||
world.browser.execute_script("$('input.file-input').css('display', 'block')")
|
||||
@@ -388,4 +394,3 @@ def create_other_user(_step, name, has_extra_perms, role_name):
|
||||
@step('I log out')
|
||||
def log_out(_step):
|
||||
world.visit('logout')
|
||||
|
||||
|
||||
@@ -52,19 +52,13 @@ def see_a_multi_step_component(step, category):
|
||||
world.wait_for(lambda _: len(world.css_find(selector)) == len(step.hashes))
|
||||
|
||||
for idx, step_hash in enumerate(step.hashes):
|
||||
|
||||
if category == 'HTML':
|
||||
html_matcher = {
|
||||
'Text':
|
||||
'\n \n',
|
||||
'Announcement':
|
||||
'<p> Words of encouragement! This is a short note that most students will read. </p>',
|
||||
'Zooming Image':
|
||||
'<h2>ZOOMING DIAGRAMS</h2>',
|
||||
'E-text Written in LaTeX':
|
||||
'<h2>Example: E-text page</h2>',
|
||||
'Raw HTML':
|
||||
'<p>This template is similar to the Text template. The only difference is',
|
||||
'Text': '\n \n',
|
||||
'Announcement': '<p> Words of encouragement! This is a short note that most students will read. </p>',
|
||||
'Zooming Image': '<h2>ZOOMING DIAGRAMS</h2>',
|
||||
'E-text Written in LaTeX': '<h2>Example: E-text page</h2>',
|
||||
'Raw HTML': '<p>This template is similar to the Text template. The only difference is',
|
||||
}
|
||||
actual_html = world.css_html(selector, index=idx)
|
||||
assert_in(html_matcher[step_hash['Component']], actual_html)
|
||||
|
||||
@@ -93,8 +93,10 @@ def click_component_from_menu(category, component_type, is_advanced):
|
||||
"""
|
||||
if is_advanced:
|
||||
# Sometimes this click does not work if you go too fast.
|
||||
world.retry_on_exception(_click_advanced,
|
||||
ignored_exceptions=AssertionError)
|
||||
world.retry_on_exception(
|
||||
_click_advanced,
|
||||
ignored_exceptions=AssertionError,
|
||||
)
|
||||
|
||||
# Retry this in case the list is empty because you tried too fast.
|
||||
link = world.retry_on_exception(
|
||||
|
||||
@@ -24,7 +24,8 @@ def i_export_the_course(step):
|
||||
|
||||
@step('I edit and enter bad XML$')
|
||||
def i_enter_bad_xml(step):
|
||||
enter_xml_in_advanced_problem(step,
|
||||
enter_xml_in_advanced_problem(
|
||||
step,
|
||||
"""<problem><h1>Smallest Canvas</h1>
|
||||
<p>You want to make the smallest canvas you can.</p>
|
||||
<multiplechoiceresponse>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# pylint: disable=C0111
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
from lettuce import world, step
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
@@ -20,9 +20,10 @@ SELECTORS = {
|
||||
# We should wait 300 ms for event handler invocation + 200ms for safety.
|
||||
DELAY = 0.5
|
||||
|
||||
|
||||
@step('youtube stub server (.*) YouTube API')
|
||||
def configure_youtube_api(_step, action):
|
||||
action=action.strip()
|
||||
action = action.strip()
|
||||
if action == 'proxies':
|
||||
world.youtube.config['youtube_api_blocked'] = False
|
||||
elif action == 'blocks':
|
||||
@@ -30,6 +31,7 @@ def configure_youtube_api(_step, action):
|
||||
else:
|
||||
raise ValueError('Parameter `action` should be one of "proxies" or "blocks".')
|
||||
|
||||
|
||||
@step('I have created a Video component$')
|
||||
def i_created_a_video_component(step):
|
||||
step.given('I am in Studio editing a new unit')
|
||||
@@ -47,6 +49,7 @@ def i_created_a_video_component(step):
|
||||
if not world.youtube.config.get('youtube_api_blocked'):
|
||||
world.wait_for_visible(SELECTORS['controls'])
|
||||
|
||||
|
||||
@step('I have created a Video component with subtitles$')
|
||||
def i_created_a_video_with_subs(_step):
|
||||
_step.given('I have created a Video component with subtitles "OEoXaMPEzfM"')
|
||||
@@ -221,7 +224,7 @@ def see_a_range_slider_with_proper_range(_step):
|
||||
def do_not_see_or_not_button_video(_step, action, button_type):
|
||||
world.wait(DELAY)
|
||||
world.wait_for_ajax_complete()
|
||||
action=action.strip()
|
||||
action = action.strip()
|
||||
button = button_type.strip()
|
||||
if action == 'do not':
|
||||
assert not world.is_css_present(VIDEO_BUTTONS[button])
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# pylint: disable=C0111
|
||||
|
||||
from lettuce import world, step
|
||||
from nose.tools import assert_true # pylint: disable=E0611
|
||||
from nose.tools import assert_true # pylint: disable=E0611
|
||||
from video_editor import RequestHandlerWithSessionId, success_upload_file
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '''Delete a MongoDB backed course'''
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class Command(BaseCommand):
|
||||
try:
|
||||
course_key = SlashSeparatedCourseKey.from_deprecated_string(args[0])
|
||||
except InvalidKeyError:
|
||||
raise CommandError(GitExportError.BAD_COURSE)
|
||||
raise CommandError(_(GitExportError.BAD_COURSE))
|
||||
|
||||
try:
|
||||
git_export_utils.export_to_git(
|
||||
@@ -72,4 +72,4 @@ class Command(BaseCommand):
|
||||
options.get('rdir', None)
|
||||
)
|
||||
except git_export_utils.GitExportError as ex:
|
||||
raise CommandError(str(ex))
|
||||
raise CommandError(_(ex.message))
|
||||
|
||||
@@ -11,6 +11,8 @@ from django.db.utils import IntegrityError
|
||||
from student.roles import CourseInstructorRole, CourseStaffRole
|
||||
|
||||
#------------ to run: ./manage.py cms populate_creators --settings=dev
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
Script for granting existing course instructors course creator privileges.
|
||||
|
||||
@@ -11,8 +11,13 @@ def query_yes_no(question, default="yes"):
|
||||
|
||||
The "answer" return value is one of "yes" or "no".
|
||||
"""
|
||||
valid = {"yes": True, "y": True, "ye": True,
|
||||
"no": False, "n": False}
|
||||
valid = {
|
||||
"yes": True,
|
||||
"y": True,
|
||||
"ye": True,
|
||||
"no": False,
|
||||
"n": False,
|
||||
}
|
||||
if default is None:
|
||||
prompt = " [y/n] "
|
||||
elif default == "yes":
|
||||
|
||||
@@ -10,4 +10,3 @@ class Command(BaseCommand):
|
||||
raise CommandError("restore_asset_from_trashcan requires one argument: <location>")
|
||||
|
||||
restore_asset_from_trashcan(args[0])
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.mongo.base import location_to_query
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.xml_importer import import_from_xml
|
||||
from django.conf import settings
|
||||
|
||||
TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
|
||||
|
||||
|
||||
class ExportAllCourses(ModuleStoreTestCase):
|
||||
@@ -30,7 +33,7 @@ class ExportAllCourses(ModuleStoreTestCase):
|
||||
import_from_xml(
|
||||
self.module_store,
|
||||
'**replace_user**',
|
||||
'common/test/data/',
|
||||
TEST_DATA_DIR,
|
||||
['dot-underscore'],
|
||||
static_content_store=self.content_store,
|
||||
do_import_static=True,
|
||||
|
||||
@@ -80,6 +80,34 @@ class TestGitExport(CourseTestCase):
|
||||
stderr=StringIO.StringIO())
|
||||
self.assertEqual(ex.exception.code, 1)
|
||||
|
||||
def test_error_output(self):
|
||||
"""
|
||||
Verify that error output is actually resolved as the correct string
|
||||
"""
|
||||
output = StringIO.StringIO()
|
||||
with self.assertRaises(SystemExit):
|
||||
with self.assertRaisesRegexp(CommandError, GitExportError.BAD_COURSE):
|
||||
call_command(
|
||||
'git_export', 'foo/bar:baz', 'silly',
|
||||
stdout=output, stderr=output
|
||||
)
|
||||
self.assertIn('Bad course location provided', output.getvalue())
|
||||
output.close()
|
||||
|
||||
output = StringIO.StringIO()
|
||||
with self.assertRaises(SystemExit):
|
||||
with self.assertRaisesRegexp(CommandError, GitExportError.URL_BAD):
|
||||
call_command(
|
||||
'git_export', 'foo/bar/baz', 'silly',
|
||||
stdout=output, stderr=output
|
||||
)
|
||||
self.assertIn(
|
||||
'Non writable git url provided. Expecting something like:'
|
||||
' git@github.com:mitocw/edx4edx_lite.git',
|
||||
output.getvalue()
|
||||
)
|
||||
output.close()
|
||||
|
||||
def test_bad_git_url(self):
|
||||
"""
|
||||
Test several bad URLs for validation
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import copy
|
||||
import mock
|
||||
from mock import patch
|
||||
import shutil
|
||||
import lxml
|
||||
|
||||
@@ -56,6 +57,8 @@ from xmodule.contentstore.content import StaticContent
|
||||
TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE)
|
||||
TEST_DATA_CONTENTSTORE['DOC_STORE_CONFIG']['db'] = 'test_xcontent_%s' % uuid4().hex
|
||||
|
||||
TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
|
||||
|
||||
|
||||
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
|
||||
class ContentStoreTestCase(CourseTestCase):
|
||||
@@ -69,7 +72,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
|
||||
Tests which legitimately need to import a course
|
||||
"""
|
||||
def test_no_static_link_rewrites_on_import(self):
|
||||
course_items = import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'])
|
||||
course_items = import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'])
|
||||
course = course_items[0]
|
||||
|
||||
handouts_usage_key = course.id.make_usage_key('course_info', 'handouts')
|
||||
@@ -81,7 +84,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
|
||||
self.assertIn('/static/', handouts.data)
|
||||
|
||||
def test_xlint_fails(self):
|
||||
err_cnt = perform_xlint('common/test/data', ['toy'])
|
||||
err_cnt = perform_xlint(TEST_DATA_DIR, ['toy'])
|
||||
self.assertGreater(err_cnt, 0)
|
||||
|
||||
def test_about_overrides(self):
|
||||
@@ -90,7 +93,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
|
||||
e.g. /about/Fall_2012/effort.html
|
||||
while there is a base definition in /about/effort.html
|
||||
'''
|
||||
course_items = import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'])
|
||||
course_items = import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'])
|
||||
course_key = course_items[0].id
|
||||
effort = self.store.get_item(course_key.make_usage_key('about', 'effort'))
|
||||
self.assertEqual(effort.data, '6 hours')
|
||||
@@ -105,7 +108,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
|
||||
'''
|
||||
content_store = contentstore()
|
||||
|
||||
import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'], static_content_store=content_store, verbose=True)
|
||||
import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'], static_content_store=content_store, verbose=True)
|
||||
|
||||
course = self.store.get_course(SlashSeparatedCourseKey('edX', 'toy', '2012_Fall'))
|
||||
|
||||
@@ -153,7 +156,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
|
||||
Test that course info updates are imported and exported with all content fields ('data', 'items')
|
||||
"""
|
||||
content_store = contentstore()
|
||||
data_dir = "common/test/data/"
|
||||
data_dir = TEST_DATA_DIR
|
||||
courses = import_from_xml(
|
||||
self.store, self.user.id, data_dir, ['course_info_updates'],
|
||||
static_content_store=content_store, verbose=True,
|
||||
@@ -166,7 +169,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
|
||||
self.assertIsNotNone(course_updates)
|
||||
|
||||
# check that course which is imported has files 'updates.html' and 'updates.items.json'
|
||||
filesystem = OSFS(data_dir + 'course_info_updates/info')
|
||||
filesystem = OSFS(data_dir + '/course_info_updates/info')
|
||||
self.assertTrue(filesystem.exists('updates.html'))
|
||||
self.assertTrue(filesystem.exists('updates.items.json'))
|
||||
|
||||
@@ -204,7 +207,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
|
||||
def test_rewrite_nonportable_links_on_import(self):
|
||||
content_store = contentstore()
|
||||
|
||||
import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'], static_content_store=content_store)
|
||||
import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'], static_content_store=content_store)
|
||||
|
||||
# first check a static asset link
|
||||
course_key = SlashSeparatedCourseKey('edX', 'toy', 'run')
|
||||
@@ -323,7 +326,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
|
||||
def test_export_course_with_metadata_only_video(self):
|
||||
content_store = contentstore()
|
||||
|
||||
import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'])
|
||||
import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'])
|
||||
course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
|
||||
|
||||
# create a new video module and add it as a child to a vertical
|
||||
@@ -352,7 +355,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
|
||||
"""
|
||||
content_store = contentstore()
|
||||
|
||||
import_from_xml(self.store, self.user.id, 'common/test/data/', ['word_cloud'])
|
||||
import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['word_cloud'])
|
||||
course_id = SlashSeparatedCourseKey('HarvardX', 'ER22x', '2013_Spring')
|
||||
|
||||
verticals = self.store.get_items(course_id, qualifiers={'category': 'vertical'})
|
||||
@@ -379,7 +382,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
|
||||
"""
|
||||
content_store = contentstore()
|
||||
|
||||
import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'])
|
||||
import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'])
|
||||
course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
|
||||
|
||||
verticals = self.store.get_items(course_id, qualifiers={'category': 'vertical'})
|
||||
@@ -410,7 +413,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
|
||||
"""
|
||||
content_store = contentstore()
|
||||
|
||||
import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'])
|
||||
import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'])
|
||||
|
||||
course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
|
||||
|
||||
@@ -432,7 +435,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
|
||||
def test_export_course_without_content_store(self):
|
||||
# Create toy course
|
||||
|
||||
course_items = import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'])
|
||||
course_items = import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'])
|
||||
course_id = course_items[0].id
|
||||
|
||||
root_dir = path(mkdtemp_clean())
|
||||
@@ -534,6 +537,7 @@ class MiscCourseTests(ContentStoreTestCase):
|
||||
for expected in expected_types:
|
||||
self.assertIn(expected, resp.content)
|
||||
|
||||
@patch('django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES', [])
|
||||
def test_advanced_components_in_edit_unit(self):
|
||||
# This could be made better, but for now let's just assert that we see the advanced modules mentioned in the page
|
||||
# response HTML
|
||||
@@ -853,15 +857,14 @@ class MiscCourseTests(ContentStoreTestCase):
|
||||
self.assertContains(resp, unicode(asset_key))
|
||||
|
||||
def test_prefetch_children(self):
|
||||
# make sure we haven't done too many round trips to DB
|
||||
# note we say 4 round trips here for:
|
||||
# make sure we haven't done too many round trips to DB:
|
||||
# 1) the course,
|
||||
# 2 & 3) for the chapters and sequentials
|
||||
# Because we're querying from the top of the tree, we cache information needed for inheritance,
|
||||
# so we don't need to make an extra query to compute it.
|
||||
# set the branch to 'publish' in order to prevent extra lookups of draft versions
|
||||
with self.store.branch_setting(ModuleStoreEnum.Branch.published_only, self.course.id):
|
||||
with check_mongo_calls(3, 0):
|
||||
with check_mongo_calls(3):
|
||||
course = self.store.get_course(self.course.id, depth=2)
|
||||
|
||||
# make sure we pre-fetched a known sequential which should be at depth=2
|
||||
@@ -873,7 +876,7 @@ class MiscCourseTests(ContentStoreTestCase):
|
||||
# Now, test with the branch set to draft. No extra round trips b/c it doesn't go deep enough to get
|
||||
# beyond direct only categories
|
||||
with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id):
|
||||
with check_mongo_calls(3, 0):
|
||||
with check_mongo_calls(3):
|
||||
self.store.get_course(self.course.id, depth=2)
|
||||
|
||||
def _check_verticals(self, locations):
|
||||
@@ -1008,7 +1011,7 @@ class ContentStoreTest(ContentStoreTestCase):
|
||||
def test_create_course_duplicate_course(self):
|
||||
"""Test new course creation - error path"""
|
||||
self.client.ajax_post('/course/', self.course_data)
|
||||
self.assert_course_creation_failed('There is already a course defined with the same organization, course number, and course run. Please change either organization or course number to be unique.')
|
||||
self.assert_course_creation_failed('There is already a course defined with the same organization and course number. Please change either organization or course number to be unique.')
|
||||
|
||||
def assert_course_creation_failed(self, error_message):
|
||||
"""
|
||||
@@ -1037,7 +1040,7 @@ class ContentStoreTest(ContentStoreTestCase):
|
||||
self.course_data['display_name'] = 'Robot Super Course Two'
|
||||
self.course_data['run'] = '2013_Summer'
|
||||
|
||||
self.assert_course_creation_failed('There is already a course defined with the same organization, course number, and course run. Please change either organization or course number to be unique.')
|
||||
self.assert_course_creation_failed('There is already a course defined with the same organization and course number. Please change either organization or course number to be unique.')
|
||||
|
||||
def test_create_course_case_change(self):
|
||||
"""Test new course creation - error path due to case insensitive name equality"""
|
||||
@@ -1045,13 +1048,13 @@ class ContentStoreTest(ContentStoreTestCase):
|
||||
self.client.ajax_post('/course/', self.course_data)
|
||||
cache_current = self.course_data['org']
|
||||
self.course_data['org'] = self.course_data['org'].lower()
|
||||
self.assert_course_creation_failed('There is already a course defined with the same organization, course number, and course run. Please change either organization or course number to be unique.')
|
||||
self.assert_course_creation_failed('There is already a course defined with the same organization and course number. Please change either organization or course number to be unique.')
|
||||
self.course_data['org'] = cache_current
|
||||
|
||||
self.client.ajax_post('/course/', self.course_data)
|
||||
cache_current = self.course_data['number']
|
||||
self.course_data['number'] = self.course_data['number'].upper()
|
||||
self.assert_course_creation_failed('There is already a course defined with the same organization, course number, and course run. Please change either organization or course number to be unique.')
|
||||
self.assert_course_creation_failed('There is already a course defined with the same organization and course number. Please change either organization or course number to be unique.')
|
||||
|
||||
def test_course_substring(self):
|
||||
"""
|
||||
@@ -1221,7 +1224,7 @@ class ContentStoreTest(ContentStoreTestCase):
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
course_items = import_from_xml(self.store, self.user.id, 'common/test/data/', ['simple'])
|
||||
course_items = import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['simple'])
|
||||
course_key = course_items[0].id
|
||||
|
||||
resp = self._show_course_overview(course_key)
|
||||
@@ -1268,7 +1271,7 @@ class ContentStoreTest(ContentStoreTestCase):
|
||||
target_course_id = _get_course_id(self.course_data)
|
||||
_create_course(self, target_course_id, self.course_data)
|
||||
|
||||
import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'], target_course_id=target_course_id)
|
||||
import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'], target_course_id=target_course_id)
|
||||
|
||||
modules = self.store.get_items(target_course_id)
|
||||
|
||||
@@ -1303,7 +1306,7 @@ class ContentStoreTest(ContentStoreTestCase):
|
||||
course_module.save()
|
||||
|
||||
# Import a course with wiki_slug == location.course
|
||||
import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'], target_course_id=target_course_id)
|
||||
import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'], target_course_id=target_course_id)
|
||||
course_module = self.store.get_course(target_course_id)
|
||||
self.assertEquals(course_module.wiki_slug, 'toy')
|
||||
|
||||
@@ -1318,17 +1321,17 @@ class ContentStoreTest(ContentStoreTestCase):
|
||||
_create_course(self, target_course_id, course_data)
|
||||
|
||||
# Import a course with wiki_slug == location.course
|
||||
import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'], target_course_id=target_course_id)
|
||||
import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'], target_course_id=target_course_id)
|
||||
course_module = self.store.get_course(target_course_id)
|
||||
self.assertEquals(course_module.wiki_slug, 'MITx.111.2013_Spring')
|
||||
|
||||
# Now try importing a course with wiki_slug == '{0}.{1}.{2}'.format(location.org, location.course, location.run)
|
||||
import_from_xml(self.store, self.user.id, 'common/test/data/', ['two_toys'], target_course_id=target_course_id)
|
||||
import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['two_toys'], target_course_id=target_course_id)
|
||||
course_module = self.store.get_course(target_course_id)
|
||||
self.assertEquals(course_module.wiki_slug, 'MITx.111.2013_Spring')
|
||||
|
||||
def test_import_metadata_with_attempts_empty_string(self):
|
||||
import_from_xml(self.store, self.user.id, 'common/test/data/', ['simple'])
|
||||
import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['simple'])
|
||||
did_load_item = False
|
||||
try:
|
||||
course_key = SlashSeparatedCourseKey('edX', 'simple', 'problem')
|
||||
@@ -1350,7 +1353,7 @@ class ContentStoreTest(ContentStoreTestCase):
|
||||
self.assertNotEquals(new_discussion_item.discussion_id, '$$GUID$$')
|
||||
|
||||
def test_metadata_inheritance(self):
|
||||
course_items = import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'])
|
||||
course_items = import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'])
|
||||
|
||||
course = course_items[0]
|
||||
verticals = self.store.get_items(course.id, qualifiers={'category': 'vertical'})
|
||||
@@ -1419,7 +1422,7 @@ class ContentStoreTest(ContentStoreTestCase):
|
||||
courses = import_from_xml(
|
||||
self.store,
|
||||
self.user.id,
|
||||
'common/test/data/',
|
||||
TEST_DATA_DIR,
|
||||
['conditional_and_poll'],
|
||||
static_content_store=content_store
|
||||
)
|
||||
|
||||
@@ -607,11 +607,13 @@ class CourseMetadataEditingTest(CourseTestCase):
|
||||
|
||||
def test_correct_http_status(self):
|
||||
json_data = json.dumps({
|
||||
"advertised_start": {"value": 1, "display_name": "Course Advertised Start Date", },
|
||||
"days_early_for_beta": {"value": "supposed to be an integer",
|
||||
"display_name": "Days Early for Beta Users", },
|
||||
"advanced_modules": {"value": 1, "display_name": "Advanced Module List", },
|
||||
})
|
||||
"advertised_start": {"value": 1, "display_name": "Course Advertised Start Date", },
|
||||
"days_early_for_beta": {
|
||||
"value": "supposed to be an integer",
|
||||
"display_name": "Days Early for Beta Users",
|
||||
},
|
||||
"advanced_modules": {"value": 1, "display_name": "Advanced Module List", },
|
||||
})
|
||||
response = self.client.ajax_post(self.course_setting_url, json_data)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
@@ -623,7 +625,7 @@ class CourseMetadataEditingTest(CourseTestCase):
|
||||
"days_early_for_beta": {"value": 2},
|
||||
},
|
||||
user=self.user
|
||||
)
|
||||
)
|
||||
self.update_check(test_model)
|
||||
# try fresh fetch to ensure persistence
|
||||
fresh = modulestore().get_course(self.course.id)
|
||||
|
||||
@@ -66,8 +66,10 @@ class TemplateTests(unittest.TestCase):
|
||||
self.assertEqual(index_info['course'], 'course')
|
||||
self.assertEqual(index_info['run'], '2014')
|
||||
|
||||
test_chapter = persistent_factories.ItemFactory.create(display_name='chapter 1',
|
||||
parent_location=test_course.location)
|
||||
test_chapter = persistent_factories.ItemFactory.create(
|
||||
display_name='chapter 1',
|
||||
parent_location=test_course.location
|
||||
)
|
||||
self.assertIsInstance(test_chapter, SequenceDescriptor)
|
||||
# refetch parent which should now point to child
|
||||
test_course = self.split_store.get_course(test_course.id.version_agnostic())
|
||||
@@ -156,8 +158,10 @@ class TemplateTests(unittest.TestCase):
|
||||
course='history', run='doomed', org='edu.harvard',
|
||||
display_name='doomed test course',
|
||||
user_id='testbot')
|
||||
persistent_factories.ItemFactory.create(display_name='chapter 1',
|
||||
parent_location=test_course.location)
|
||||
persistent_factories.ItemFactory.create(
|
||||
display_name='chapter 1',
|
||||
parent_location=test_course.location
|
||||
)
|
||||
|
||||
id_locator = test_course.id.for_branch(ModuleStoreEnum.BranchName.draft)
|
||||
guid_locator = test_course.location.course_agnostic()
|
||||
@@ -180,10 +184,17 @@ class TemplateTests(unittest.TestCase):
|
||||
display_name='history test course',
|
||||
user_id='testbot'
|
||||
)
|
||||
chapter = persistent_factories.ItemFactory.create(display_name='chapter 1',
|
||||
parent_location=test_course.location, user_id='testbot')
|
||||
sub = persistent_factories.ItemFactory.create(display_name='subsection 1',
|
||||
parent_location=chapter.location, user_id='testbot', category='vertical')
|
||||
chapter = persistent_factories.ItemFactory.create(
|
||||
display_name='chapter 1',
|
||||
parent_location=test_course.location,
|
||||
user_id='testbot'
|
||||
)
|
||||
sub = persistent_factories.ItemFactory.create(
|
||||
display_name='subsection 1',
|
||||
parent_location=chapter.location,
|
||||
user_id='testbot',
|
||||
category='vertical'
|
||||
)
|
||||
first_problem = persistent_factories.ItemFactory.create(
|
||||
display_name='problem 1', parent_location=sub.location, user_id='testbot', category='problem',
|
||||
data="<problem></problem>"
|
||||
|
||||
@@ -55,10 +55,11 @@ class InternationalizationTest(ModuleStoreTestCase):
|
||||
self.client = AjaxEnabledTestClient()
|
||||
self.client.login(username=self.uname, password=self.password)
|
||||
|
||||
resp = self.client.get_html('/course/',
|
||||
{},
|
||||
HTTP_ACCEPT_LANGUAGE='en'
|
||||
)
|
||||
resp = self.client.get_html(
|
||||
'/course/',
|
||||
{},
|
||||
HTTP_ACCEPT_LANGUAGE='en',
|
||||
)
|
||||
|
||||
self.assertContains(resp,
|
||||
'<h1 class="page-header">My Courses</h1>',
|
||||
|
||||
@@ -24,6 +24,8 @@ from uuid import uuid4
|
||||
TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE)
|
||||
TEST_DATA_CONTENTSTORE['DOC_STORE_CONFIG']['db'] = 'test_xcontent_%s' % uuid4().hex
|
||||
|
||||
TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
|
||||
@@ -48,7 +50,7 @@ class ContentStoreImportTest(ModuleStoreTestCase):
|
||||
import_from_xml(
|
||||
module_store,
|
||||
self.user.id,
|
||||
'common/test/data/',
|
||||
TEST_DATA_DIR,
|
||||
['test_import_course'],
|
||||
static_content_store=content_store,
|
||||
do_import_static=False,
|
||||
@@ -70,7 +72,7 @@ class ContentStoreImportTest(ModuleStoreTestCase):
|
||||
course_items = import_from_xml(
|
||||
module_store,
|
||||
self.user.id,
|
||||
'common/test/data',
|
||||
TEST_DATA_DIR,
|
||||
['test_import_course_2'],
|
||||
target_course_id=course.id,
|
||||
verbose=True,
|
||||
@@ -86,7 +88,7 @@ class ContentStoreImportTest(ModuleStoreTestCase):
|
||||
import_from_xml(
|
||||
module_store,
|
||||
self.user.id,
|
||||
'common/test/data/',
|
||||
TEST_DATA_DIR,
|
||||
['2014_Uni'],
|
||||
target_course_id=course_id
|
||||
)
|
||||
@@ -131,7 +133,7 @@ class ContentStoreImportTest(ModuleStoreTestCase):
|
||||
content_store = contentstore()
|
||||
|
||||
module_store = modulestore()
|
||||
import_from_xml(module_store, self.user.id, 'common/test/data/', ['toy'], static_content_store=content_store, do_import_static=False, verbose=True)
|
||||
import_from_xml(module_store, self.user.id, TEST_DATA_DIR, ['toy'], static_content_store=content_store, do_import_static=False, verbose=True)
|
||||
|
||||
course = module_store.get_course(SlashSeparatedCourseKey('edX', 'toy', '2012_Fall'))
|
||||
|
||||
@@ -142,7 +144,7 @@ class ContentStoreImportTest(ModuleStoreTestCase):
|
||||
|
||||
def test_no_static_link_rewrites_on_import(self):
|
||||
module_store = modulestore()
|
||||
courses = import_from_xml(module_store, self.user.id, 'common/test/data/', ['toy'], do_import_static=False, verbose=True)
|
||||
courses = import_from_xml(module_store, self.user.id, TEST_DATA_DIR, ['toy'], do_import_static=False, verbose=True)
|
||||
course_key = courses[0].id
|
||||
|
||||
handouts = module_store.get_item(course_key.make_usage_key('course_info', 'handouts'))
|
||||
@@ -183,7 +185,7 @@ class ContentStoreImportTest(ModuleStoreTestCase):
|
||||
import_from_xml(
|
||||
module_store,
|
||||
self.user.id,
|
||||
'common/test/data/',
|
||||
TEST_DATA_DIR,
|
||||
['conditional'],
|
||||
target_course_id=target_course_id
|
||||
)
|
||||
@@ -213,7 +215,7 @@ class ContentStoreImportTest(ModuleStoreTestCase):
|
||||
import_from_xml(
|
||||
module_store,
|
||||
self.user.id,
|
||||
'common/test/data/',
|
||||
TEST_DATA_DIR,
|
||||
['open_ended'],
|
||||
target_course_id=target_course_id
|
||||
)
|
||||
@@ -254,7 +256,7 @@ class ContentStoreImportTest(ModuleStoreTestCase):
|
||||
import_from_xml(
|
||||
module_store,
|
||||
self.user.id,
|
||||
'common/test/data/',
|
||||
TEST_DATA_DIR,
|
||||
[source_course_name],
|
||||
target_course_id=target_course_id
|
||||
)
|
||||
|
||||
@@ -2,6 +2,9 @@ from xmodule.modulestore.xml_importer import import_from_xml
|
||||
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from django.conf import settings
|
||||
|
||||
TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
|
||||
|
||||
|
||||
# This test is in the CMS module because the test configuration to use a draft
|
||||
@@ -10,7 +13,7 @@ class DraftReorderTestCase(ModuleStoreTestCase):
|
||||
|
||||
def test_order(self):
|
||||
store = modulestore()
|
||||
course_items = import_from_xml(store, self.user.id, 'common/test/data/', ['import_draft_order'])
|
||||
course_items = import_from_xml(store, self.user.id, TEST_DATA_DIR, ['import_draft_order'])
|
||||
course_key = course_items[0].id
|
||||
sequential = store.get_item(course_key.make_usage_key('sequential', '0f4f7649b10141b0bdc9922dcf94515a'))
|
||||
verticals = sequential.children
|
||||
|
||||
@@ -8,6 +8,9 @@ from xblock.fields import String
|
||||
from xmodule.modulestore.xml_importer import import_from_xml
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.mongo.draft import as_draft
|
||||
from django.conf import settings
|
||||
|
||||
TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
|
||||
|
||||
|
||||
class StubXBlock(XBlock):
|
||||
@@ -58,7 +61,7 @@ class XBlockImportTest(ModuleStoreTestCase):
|
||||
|
||||
"""
|
||||
courses = import_from_xml(
|
||||
self.store, self.user.id, 'common/test/data', [course_dir]
|
||||
self.store, self.user.id, TEST_DATA_DIR, [course_dir]
|
||||
)
|
||||
|
||||
xblock_location = courses[0].id.make_usage_key('stubxblock', 'xblock_test')
|
||||
|
||||
@@ -32,13 +32,14 @@ class TestCourseAccess(ModuleStoreTestCase):
|
||||
# create a course via the view handler which has a different strategy for permissions than the factory
|
||||
self.course_key = SlashSeparatedCourseKey('myu', 'mydept.mycourse', 'myrun')
|
||||
course_url = reverse_url('course_handler')
|
||||
self.client.ajax_post(course_url,
|
||||
self.client.ajax_post(
|
||||
course_url,
|
||||
{
|
||||
'org': self.course_key.org,
|
||||
'number': self.course_key.course,
|
||||
'display_name': 'My favorite course',
|
||||
'run': self.course_key.run,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
self.users = self._create_users()
|
||||
|
||||
@@ -161,7 +161,6 @@ class TestDownloadYoutubeSubs(ModuleStoreTestCase):
|
||||
number = '999'
|
||||
display_name = 'Test course'
|
||||
|
||||
|
||||
def clear_sub_content(self, subs_id):
|
||||
"""
|
||||
Remove, if subtitle content exists.
|
||||
@@ -472,6 +471,7 @@ class TestYoutubeTranscripts(unittest.TestCase):
|
||||
self.assertEqual(transcripts, expected_transcripts)
|
||||
mock_get.assert_called_with('http://video.google.com/timedtext', params={'lang': 'en', 'v': 'good_youtube_id'})
|
||||
|
||||
|
||||
class TestTranscript(unittest.TestCase):
|
||||
"""
|
||||
Tests for Transcript class e.g. different transcript conversions.
|
||||
@@ -489,7 +489,6 @@ class TestTranscript(unittest.TestCase):
|
||||
|
||||
""")
|
||||
|
||||
|
||||
self.sjson_transcript = textwrap.dedent("""\
|
||||
{
|
||||
"start": [
|
||||
|
||||
@@ -86,6 +86,7 @@ class LMSLinksTestCase(TestCase):
|
||||
link = utils.get_lms_link_for_item(location)
|
||||
self.assertEquals(link, "//localhost:8000/courses/mitX/101/test/jump_to/i4x://mitX/101/course/test")
|
||||
|
||||
|
||||
class ExtraPanelTabTestCase(TestCase):
|
||||
""" Tests adding and removing extra course tabs. """
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ from student.models import Registration
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey, AssetLocation
|
||||
from contentstore.utils import reverse_url
|
||||
from xmodule.modulestore.split_mongo.split import SplitMongoModuleStore
|
||||
from django.conf import settings
|
||||
|
||||
TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
|
||||
|
||||
|
||||
def parse_json(response):
|
||||
@@ -137,7 +140,7 @@ class CourseTestCase(ModuleStoreTestCase):
|
||||
Imports the test toy course and populates it with additional test data
|
||||
"""
|
||||
content_store = contentstore()
|
||||
import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'], static_content_store=content_store)
|
||||
import_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'], static_content_store=content_store)
|
||||
course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
|
||||
|
||||
# create an Orphan
|
||||
|
||||
@@ -190,8 +190,8 @@ def _upload_asset(request, course_key):
|
||||
|
||||
# first let's see if a thumbnail can be created
|
||||
(thumbnail_content, thumbnail_location) = contentstore().generate_thumbnail(
|
||||
content,
|
||||
tempfile_path=tempfile_path
|
||||
content,
|
||||
tempfile_path=tempfile_path,
|
||||
)
|
||||
|
||||
# delete cached thumbnail even if one couldn't be created this time (else
|
||||
|
||||
@@ -124,6 +124,7 @@ def expand_checklist_action_url(course_module, checklist):
|
||||
|
||||
return expanded_checklist
|
||||
|
||||
|
||||
def localize_checklist_text(checklist):
|
||||
"""
|
||||
Localize texts for a given checklist and returns the modified version.
|
||||
|
||||
@@ -56,6 +56,13 @@ ADVANCED_COMPONENT_POLICY_KEY = 'advanced_modules'
|
||||
ADVANCED_PROBLEM_TYPES = settings.ADVANCED_PROBLEM_TYPES
|
||||
|
||||
|
||||
def _advanced_component_types():
|
||||
"""
|
||||
Return advanced component types which can be created.
|
||||
"""
|
||||
return [c_type for c_type in ADVANCED_COMPONENT_TYPES if c_type not in settings.DEPRECATED_ADVANCED_COMPONENT_TYPES]
|
||||
|
||||
|
||||
@require_GET
|
||||
@login_required
|
||||
def subsection_handler(request, usage_key_string):
|
||||
@@ -292,10 +299,11 @@ def get_component_templates(course):
|
||||
# enabled for the course.
|
||||
course_advanced_keys = course.advanced_modules
|
||||
advanced_component_templates = {"type": "advanced", "templates": [], "display_name": _("Advanced")}
|
||||
advanced_component_types = _advanced_component_types()
|
||||
# Set component types according to course policy file
|
||||
if isinstance(course_advanced_keys, list):
|
||||
for category in course_advanced_keys:
|
||||
if category in ADVANCED_COMPONENT_TYPES and not category in categories:
|
||||
if category in advanced_component_types and not category in categories:
|
||||
# boilerplates not supported for advanced components
|
||||
try:
|
||||
component_display_name = xblock_type_display_name(category, default_display_name=category)
|
||||
|
||||
@@ -261,6 +261,7 @@ def course_rerun_handler(request, course_key_string):
|
||||
'allow_unicode_course_id': settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID', False)
|
||||
})
|
||||
|
||||
|
||||
def _course_outline_json(request, course_module):
|
||||
"""
|
||||
Returns a JSON representation of the course module and recursively all of its children.
|
||||
@@ -386,10 +387,13 @@ def course_listing(request):
|
||||
'run': uca.course_key.run,
|
||||
'is_failed': True if uca.state == CourseRerunUIStateManager.State.FAILED else False,
|
||||
'is_in_progress': True if uca.state == CourseRerunUIStateManager.State.IN_PROGRESS else False,
|
||||
'dismiss_link':
|
||||
reverse_course_url('course_notifications_handler', uca.course_key, kwargs={
|
||||
'dismiss_link': reverse_course_url(
|
||||
'course_notifications_handler',
|
||||
uca.course_key,
|
||||
kwargs={
|
||||
'action_state_id': uca.id,
|
||||
}) if uca.state == CourseRerunUIStateManager.State.FAILED else ''
|
||||
},
|
||||
) if uca.state == CourseRerunUIStateManager.State.FAILED else ''
|
||||
}
|
||||
|
||||
# remove any courses in courses that are also in the in_process_course_actions list
|
||||
@@ -455,10 +459,13 @@ def course_index(request, course_key):
|
||||
'rerun_notification_id': current_action.id if current_action else None,
|
||||
'course_release_date': course_release_date,
|
||||
'settings_url': settings_url,
|
||||
'notification_dismiss_url':
|
||||
reverse_course_url('course_notifications_handler', current_action.course_key, kwargs={
|
||||
'notification_dismiss_url': reverse_course_url(
|
||||
'course_notifications_handler',
|
||||
current_action.course_key,
|
||||
kwargs={
|
||||
'action_state_id': current_action.id,
|
||||
}) if current_action else None,
|
||||
},
|
||||
) if current_action else None,
|
||||
})
|
||||
|
||||
|
||||
@@ -542,7 +549,7 @@ def _create_or_rerun_course(request):
|
||||
return JsonResponse({
|
||||
'ErrMsg': _(
|
||||
'There is already a course defined with the same '
|
||||
'organization, course number, and course run. Please '
|
||||
'organization and course number. Please '
|
||||
'change either organization or course number to be unique.'
|
||||
),
|
||||
'OrgErrMsg': _(
|
||||
@@ -1290,10 +1297,12 @@ class GroupConfiguration(object):
|
||||
'container_handler',
|
||||
course.location.course_key.make_usage_key(unit.location.block_type, unit.location.name)
|
||||
)
|
||||
|
||||
validation_summary = split_test.general_validation_message()
|
||||
usage_info[split_test.user_partition_id].append({
|
||||
'label': '{} / {}'.format(unit.display_name, split_test.display_name),
|
||||
'url': unit_url,
|
||||
'validation': split_test.general_validation_message,
|
||||
'validation': validation_summary.to_json() if validation_summary else None,
|
||||
})
|
||||
return usage_info
|
||||
|
||||
|
||||
@@ -31,8 +31,6 @@ from xmodule.modulestore.xml_exporter import export_to_xml
|
||||
from .access import has_course_access
|
||||
|
||||
from extract_tar import safetar_extractall
|
||||
from student import auth
|
||||
from student.roles import CourseInstructorRole, CourseStaffRole, GlobalStaff
|
||||
from util.json_request import JsonResponse
|
||||
from util.views import ensure_valid_course_key
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ def _preview_module_system(request, descriptor):
|
||||
replace_urls=partial(static_replace.replace_static_urls, data_directory=None, course_id=course_id),
|
||||
user=request.user,
|
||||
can_execute_unsafe_code=(lambda: can_execute_unsafe_code(course_id)),
|
||||
get_python_lib_zip=(lambda :get_python_lib_zip(contentstore, course_id)),
|
||||
get_python_lib_zip=(lambda: get_python_lib_zip(contentstore, course_id)),
|
||||
mixins=settings.XBLOCK_MIXINS,
|
||||
course_id=course_id,
|
||||
anonymous_student_id='student',
|
||||
|
||||
@@ -20,6 +20,7 @@ from ..utils import get_lms_link_for_item
|
||||
|
||||
__all__ = ['tabs_handler']
|
||||
|
||||
|
||||
@expect_json
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
@@ -203,4 +204,3 @@ def primitive_insert(course, num, tab_type, name):
|
||||
tabs = course.tabs
|
||||
tabs.insert(num, new_tab)
|
||||
modulestore().update_item(course, ModuleStoreEnum.UserID.primitive_command)
|
||||
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
"""
|
||||
Unit tests for the asset upload endpoint.
|
||||
"""
|
||||
|
||||
# pylint: disable=C0111
|
||||
# pylint: disable=W0621
|
||||
# pylint: disable=W0212
|
||||
|
||||
from datetime import datetime
|
||||
from io import BytesIO
|
||||
from pytz import UTC
|
||||
@@ -13,7 +8,7 @@ import json
|
||||
from contentstore.tests.utils import CourseTestCase
|
||||
from contentstore.views import assets
|
||||
from contentstore.utils import reverse_course_url
|
||||
from xmodule.assetstore.assetmgr import UnknownAssetType, AssetMetadataFoundTemporary
|
||||
from xmodule.assetstore.assetmgr import AssetMetadataFoundTemporary
|
||||
from xmodule.assetstore import AssetMetadata
|
||||
from xmodule.contentstore.content import StaticContent
|
||||
from xmodule.contentstore.django import contentstore
|
||||
@@ -21,6 +16,9 @@ from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.xml_importer import import_from_xml
|
||||
from django.test.utils import override_settings
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey, AssetLocation
|
||||
from django.conf import settings
|
||||
|
||||
TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
|
||||
|
||||
|
||||
class AssetsTestCase(CourseTestCase):
|
||||
@@ -32,12 +30,18 @@ class AssetsTestCase(CourseTestCase):
|
||||
self.url = reverse_course_url('assets_handler', self.course.id)
|
||||
|
||||
def upload_asset(self, name="asset-1"):
|
||||
"""
|
||||
Post to the asset upload url
|
||||
"""
|
||||
f = BytesIO(name)
|
||||
f.name = name + ".txt"
|
||||
return self.client.post(self.url, {"name": name, "file": f})
|
||||
|
||||
|
||||
class BasicAssetsTestCase(AssetsTestCase):
|
||||
"""
|
||||
Test getting assets via html w/o additional args
|
||||
"""
|
||||
def test_basic(self):
|
||||
resp = self.client.get(self.url, HTTP_ACCEPT='text/html')
|
||||
self.assertEquals(resp.status_code, 200)
|
||||
@@ -54,7 +58,7 @@ class BasicAssetsTestCase(AssetsTestCase):
|
||||
course_items = import_from_xml(
|
||||
module_store,
|
||||
self.user.id,
|
||||
'common/test/data/',
|
||||
TEST_DATA_DIR,
|
||||
['toy'],
|
||||
static_content_store=contentstore(),
|
||||
verbose=True
|
||||
@@ -78,6 +82,9 @@ class PaginationTestCase(AssetsTestCase):
|
||||
Tests the pagination of assets returned from the REST API.
|
||||
"""
|
||||
def test_json_responses(self):
|
||||
"""
|
||||
Test the ajax asset interfaces
|
||||
"""
|
||||
self.upload_asset("asset-1")
|
||||
self.upload_asset("asset-2")
|
||||
self.upload_asset("asset-3")
|
||||
@@ -97,20 +104,26 @@ class PaginationTestCase(AssetsTestCase):
|
||||
self.assert_correct_asset_response(self.url + "?page_size=3&page=1", 0, 3, 3)
|
||||
|
||||
def assert_correct_asset_response(self, url, expected_start, expected_length, expected_total):
|
||||
"""
|
||||
Get from the url and ensure it contains the expected number of responses
|
||||
"""
|
||||
resp = self.client.get(url, HTTP_ACCEPT='application/json')
|
||||
json_response = json.loads(resp.content)
|
||||
assets = json_response['assets']
|
||||
assets_response = json_response['assets']
|
||||
self.assertEquals(json_response['start'], expected_start)
|
||||
self.assertEquals(len(assets), expected_length)
|
||||
self.assertEquals(len(assets_response), expected_length)
|
||||
self.assertEquals(json_response['totalCount'], expected_total)
|
||||
|
||||
def assert_correct_sort_response(self, url, sort, direction):
|
||||
"""
|
||||
Get from the url w/ a sort option and ensure items honor that sort
|
||||
"""
|
||||
resp = self.client.get(url + '?sort=' + sort + '&direction=' + direction, HTTP_ACCEPT='application/json')
|
||||
json_response = json.loads(resp.content)
|
||||
assets = json_response['assets']
|
||||
name1 = assets[0][sort]
|
||||
name2 = assets[1][sort]
|
||||
name3 = assets[2][sort]
|
||||
assets_response = json_response['assets']
|
||||
name1 = assets_response[0][sort]
|
||||
name2 = assets_response[1][sort]
|
||||
name3 = assets_response[2][sort]
|
||||
if direction == 'asc':
|
||||
self.assertLessEqual(name1, name2)
|
||||
self.assertLessEqual(name2, name3)
|
||||
@@ -160,12 +173,6 @@ class DownloadTestCase(AssetsTestCase):
|
||||
resp = self.client.get(url, HTTP_ACCEPT='text/html')
|
||||
self.assertEquals(resp.status_code, 404)
|
||||
|
||||
def test_download_unknown_asset_type(self):
|
||||
# Change the asset type to something unknown.
|
||||
url = self.uploaded_url.replace('/asset/', '/unknown_type/')
|
||||
with self.assertRaises((UnknownAssetType, NameError)):
|
||||
self.client.get(url, HTTP_ACCEPT='text/html')
|
||||
|
||||
def test_metadata_found_in_modulestore(self):
|
||||
# Insert asset metadata into the modulestore (with no accompanying asset).
|
||||
asset_key = self.course.id.make_asset_key(AssetMetadata.ASSET_TYPE, 'pic1.jpg')
|
||||
@@ -176,7 +183,7 @@ class DownloadTestCase(AssetsTestCase):
|
||||
'curr_version': '14',
|
||||
'prev_version': '13'
|
||||
})
|
||||
modulestore().save_asset_metadata(self.course.id, asset_md, 15)
|
||||
modulestore().save_asset_metadata(asset_md, 15)
|
||||
# Get the asset metadata and have it be found in the modulestore.
|
||||
# Currently, no asset metadata should be found in the modulestore. The code is not yet storing it there.
|
||||
# If asset metadata *is* found there, an exception is raised. This test ensures the exception is indeed raised.
|
||||
@@ -198,6 +205,7 @@ class AssetToJsonTestCase(AssetsTestCase):
|
||||
location = course_key.make_asset_key('asset', 'my_file_name.jpg')
|
||||
thumbnail_location = course_key.make_asset_key('thumbnail', 'my_file_name_thumb.jpg')
|
||||
|
||||
# pylint: disable=protected-access
|
||||
output = assets._get_asset_json("my_file", upload_date, location, thumbnail_location, True)
|
||||
|
||||
self.assertEquals(output["display_name"], "my_file")
|
||||
@@ -236,6 +244,7 @@ class LockAssetTestCase(AssetsTestCase):
|
||||
|
||||
resp = self.client.post(
|
||||
url,
|
||||
# pylint: disable=protected-access
|
||||
json.dumps(assets._get_asset_json("sample_static.txt", upload_date, asset_location, None, lock)),
|
||||
"application/json"
|
||||
)
|
||||
@@ -247,7 +256,7 @@ class LockAssetTestCase(AssetsTestCase):
|
||||
course_items = import_from_xml(
|
||||
module_store,
|
||||
self.user.id,
|
||||
'common/test/data/',
|
||||
TEST_DATA_DIR,
|
||||
['toy'],
|
||||
static_content_store=contentstore(),
|
||||
verbose=True
|
||||
|
||||
@@ -8,20 +8,17 @@ import datetime
|
||||
from contentstore.tests.utils import CourseTestCase
|
||||
from contentstore.utils import reverse_course_url, add_instructor
|
||||
from contentstore.views.access import has_course_access
|
||||
from contentstore.views.course import course_outline_initial_state, _course_outline_json
|
||||
from contentstore.views.course import course_outline_initial_state
|
||||
from contentstore.views.item import create_xblock_info, VisibilityState
|
||||
from course_action_state.models import CourseRerunState
|
||||
from util.date_utils import get_default_time_display
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls, \
|
||||
mongo_uses_error_check
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
from student.tests.factories import UserFactory
|
||||
from course_action_state.managers import CourseRerunUIStateManager
|
||||
from django.conf import settings
|
||||
import ddt
|
||||
import threading
|
||||
import pytz
|
||||
|
||||
|
||||
@@ -312,28 +309,3 @@ class TestCourseOutline(CourseTestCase):
|
||||
|
||||
self.assertEqual(_get_release_date(response), get_default_time_display(self.course.start))
|
||||
_assert_settings_link_present(response)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class OutlinePerfTest(TestCourseOutline):
|
||||
def setUp(self):
|
||||
with modulestore().default_store(ModuleStoreEnum.Type.split):
|
||||
super(OutlinePerfTest, self).setUp()
|
||||
|
||||
@ddt.data(1, 2, 4, 8)
|
||||
def test_query_counts(self, num_threads):
|
||||
"""
|
||||
Test that increasing threads does not increase query counts
|
||||
"""
|
||||
def test_client():
|
||||
with modulestore().default_store(ModuleStoreEnum.Type.split):
|
||||
with modulestore().bulk_operations(self.course.id):
|
||||
course = modulestore().get_course(self.course.id, depth=0)
|
||||
return _course_outline_json(None, course)
|
||||
|
||||
per_thread = 4
|
||||
with check_mongo_calls(per_thread * num_threads, 0):
|
||||
outline_threads = [threading.Thread(target=test_client) for __ in xrange(num_threads)]
|
||||
[thread.start() for thread in outline_threads]
|
||||
# now wait until they all finish
|
||||
[thread.join() for thread in outline_threads]
|
||||
|
||||
@@ -9,7 +9,7 @@ from contentstore.views.course import GroupConfiguration
|
||||
from contentstore.tests.utils import CourseTestCase
|
||||
from xmodule.partitions.partitions import Group, UserPartition
|
||||
from xmodule.modulestore.tests.factories import ItemFactory
|
||||
from xmodule.split_test_module import ValidationMessage, ValidationMessageType
|
||||
from xmodule.validation import StudioValidation, StudioValidationMessage
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
|
||||
@@ -541,87 +541,75 @@ class GroupConfigurationsValidationTestCase(CourseTestCase, HelperMethods):
|
||||
def setUp(self):
|
||||
super(GroupConfigurationsValidationTestCase, self).setUp()
|
||||
|
||||
@patch('xmodule.split_test_module.SplitTestDescriptor.validation_messages')
|
||||
def test_error_message_present(self, mocked_validation_messages):
|
||||
@patch('xmodule.split_test_module.SplitTestDescriptor.validate_split_test')
|
||||
def verify_validation_add_usage_info(self, expected_result, mocked_message, mocked_validation_messages):
|
||||
"""
|
||||
Tests if validation message is present.
|
||||
Helper method for testing validation information present after add_usage_info.
|
||||
"""
|
||||
self._add_user_partitions()
|
||||
split_test = self._create_content_experiment(cid=0, name_suffix='0')[1]
|
||||
|
||||
mocked_validation_messages.return_value = [
|
||||
ValidationMessage(
|
||||
split_test,
|
||||
u"Validation message",
|
||||
ValidationMessageType.error
|
||||
)
|
||||
]
|
||||
group_configuration = GroupConfiguration.add_usage_info(self.course, self.store)[0]
|
||||
self.assertEqual(
|
||||
group_configuration['usage'][0]['validation'],
|
||||
{
|
||||
'message': u'This content experiment has issues that affect content visibility.',
|
||||
'type': 'error'
|
||||
}
|
||||
)
|
||||
validation = StudioValidation(split_test.location)
|
||||
validation.add(mocked_message)
|
||||
mocked_validation_messages.return_value = validation
|
||||
|
||||
@patch('xmodule.split_test_module.SplitTestDescriptor.validation_messages')
|
||||
def test_warning_message_present(self, mocked_validation_messages):
|
||||
group_configuration = GroupConfiguration.add_usage_info(self.course, self.store)[0]
|
||||
self.assertEqual(expected_result.to_json(), group_configuration['usage'][0]['validation'])
|
||||
|
||||
def test_error_message_present(self):
|
||||
"""
|
||||
Tests if validation message is present.
|
||||
Tests if validation message is present (error case).
|
||||
"""
|
||||
mocked_message = StudioValidationMessage(StudioValidationMessage.ERROR, u"Validation message")
|
||||
expected_result = StudioValidationMessage(
|
||||
StudioValidationMessage.ERROR, u"This content experiment has issues that affect content visibility."
|
||||
)
|
||||
self.verify_validation_add_usage_info(expected_result, mocked_message) # pylint: disable=no-value-for-parameter
|
||||
|
||||
def test_warning_message_present(self):
|
||||
"""
|
||||
Tests if validation message is present (warning case).
|
||||
"""
|
||||
mocked_message = StudioValidationMessage(StudioValidationMessage.WARNING, u"Validation message")
|
||||
expected_result = StudioValidationMessage(
|
||||
StudioValidationMessage.WARNING, u"This content experiment has issues that affect content visibility."
|
||||
)
|
||||
self.verify_validation_add_usage_info(expected_result, mocked_message) # pylint: disable=no-value-for-parameter
|
||||
|
||||
@patch('xmodule.split_test_module.SplitTestDescriptor.validate_split_test')
|
||||
def verify_validation_update_usage_info(self, expected_result, mocked_message, mocked_validation_messages):
|
||||
"""
|
||||
Helper method for testing validation information present after update_usage_info.
|
||||
"""
|
||||
self._add_user_partitions()
|
||||
split_test = self._create_content_experiment(cid=0, name_suffix='0')[1]
|
||||
|
||||
mocked_validation_messages.return_value = [
|
||||
ValidationMessage(
|
||||
split_test,
|
||||
u"Validation message",
|
||||
ValidationMessageType.warning
|
||||
)
|
||||
]
|
||||
group_configuration = GroupConfiguration.add_usage_info(self.course, self.store)[0]
|
||||
validation = StudioValidation(split_test.location)
|
||||
if mocked_message is not None:
|
||||
validation.add(mocked_message)
|
||||
mocked_validation_messages.return_value = validation
|
||||
|
||||
group_configuration = GroupConfiguration.update_usage_info(
|
||||
self.store, self.course, self.course.user_partitions[0]
|
||||
)
|
||||
self.assertEqual(
|
||||
group_configuration['usage'][0]['validation'],
|
||||
{
|
||||
'message': u'This content experiment has issues that affect content visibility.',
|
||||
'type': 'warning'
|
||||
}
|
||||
expected_result.to_json() if expected_result is not None else None,
|
||||
group_configuration['usage'][0]['validation']
|
||||
)
|
||||
|
||||
@patch('xmodule.split_test_module.SplitTestDescriptor.validation_messages')
|
||||
def test_update_usage_info(self, mocked_validation_messages):
|
||||
def test_update_usage_info(self):
|
||||
"""
|
||||
Tests if validation message is present when updating usage info.
|
||||
"""
|
||||
self._add_user_partitions()
|
||||
split_test = self._create_content_experiment(cid=0, name_suffix='0')[1]
|
||||
|
||||
mocked_validation_messages.return_value = [
|
||||
ValidationMessage(
|
||||
split_test,
|
||||
u"Validation message",
|
||||
ValidationMessageType.warning
|
||||
)
|
||||
]
|
||||
|
||||
group_configuration = GroupConfiguration.update_usage_info(self.store, self.course, self.course.user_partitions[0])
|
||||
|
||||
self.assertEqual(
|
||||
group_configuration['usage'][0]['validation'],
|
||||
{
|
||||
'message': u'This content experiment has issues that affect content visibility.',
|
||||
'type': 'warning'
|
||||
}
|
||||
mocked_message = StudioValidationMessage(StudioValidationMessage.WARNING, u"Validation message")
|
||||
expected_result = StudioValidationMessage(
|
||||
StudioValidationMessage.WARNING, u"This content experiment has issues that affect content visibility."
|
||||
)
|
||||
# pylint: disable=no-value-for-parameter
|
||||
self.verify_validation_update_usage_info(expected_result, mocked_message)
|
||||
|
||||
@patch('xmodule.split_test_module.SplitTestDescriptor.validation_messages')
|
||||
def test_update_usage_info_no_message(self, mocked_validation_messages):
|
||||
def test_update_usage_info_no_message(self):
|
||||
"""
|
||||
Tests if validation message is not present when updating usage info.
|
||||
"""
|
||||
self._add_user_partitions()
|
||||
self._create_content_experiment(cid=0, name_suffix='0')
|
||||
mocked_validation_messages.return_value = []
|
||||
group_configuration = GroupConfiguration.update_usage_info(self.store, self.course, self.course.user_partitions[0])
|
||||
self.assertEqual(group_configuration['usage'][0]['validation'], None)
|
||||
self.verify_validation_update_usage_info(None, None) # pylint: disable=no-value-for-parameter
|
||||
|
||||
@@ -130,8 +130,10 @@ class GetItemTest(ItemTest):
|
||||
root_usage_key = self._create_vertical()
|
||||
html, __ = self._get_container_preview(root_usage_key)
|
||||
|
||||
# Verify that the Studio wrapper is not added
|
||||
self.assertNotIn('wrapper-xblock', html)
|
||||
# XBlock messages are added by the Studio wrapper.
|
||||
self.assertIn('wrapper-xblock-message', html)
|
||||
# Make sure that "wrapper-xblock" does not appear by itself (without -message at end).
|
||||
self.assertNotRegexpMatches(html, r'wrapper-xblock[^-]+')
|
||||
|
||||
# Verify that the header and article tags are still added
|
||||
self.assertIn('<header class="xblock-header xblock-header-vertical">', html)
|
||||
@@ -1201,6 +1203,43 @@ class TestComponentTemplates(CourseTestCase):
|
||||
self.assertEqual(ora_template.get('category'), 'openassessment')
|
||||
self.assertIsNone(ora_template.get('boilerplate_name', None))
|
||||
|
||||
@patch('django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES', ["combinedopenended", "peergrading"])
|
||||
def test_ora1_no_advance_component_button(self):
|
||||
"""
|
||||
Test that there will be no `Advanced` button on unit page if `combinedopenended` and `peergrading` are
|
||||
deprecated provided that there are only 'combinedopenended', 'peergrading' modules in `Advanced Module List`
|
||||
"""
|
||||
self.course.advanced_modules.extend(['combinedopenended', 'peergrading'])
|
||||
templates = get_component_templates(self.course)
|
||||
button_names = [template['display_name'] for template in templates]
|
||||
self.assertNotIn('Advanced', button_names)
|
||||
|
||||
@patch('django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES', ["combinedopenended", "peergrading"])
|
||||
def test_cannot_create_ora1_problems(self):
|
||||
"""
|
||||
Test that we can't create ORA1 problems if `combinedopenended` and `peergrading` are deprecated
|
||||
"""
|
||||
self.course.advanced_modules.extend(['annotatable', 'combinedopenended', 'peergrading'])
|
||||
templates = get_component_templates(self.course)
|
||||
button_names = [template['display_name'] for template in templates]
|
||||
self.assertIn('Advanced', button_names)
|
||||
self.assertEqual(len(templates[0]['templates']), 1)
|
||||
template_display_names = [template['display_name'] for template in templates[0]['templates']]
|
||||
self.assertEqual(template_display_names, ['Annotation'])
|
||||
|
||||
@patch('django.conf.settings.DEPRECATED_ADVANCED_COMPONENT_TYPES', [])
|
||||
def test_create_ora1_problems(self):
|
||||
"""
|
||||
Test that we can create ORA1 problems if `combinedopenended` and `peergrading` are not deprecated
|
||||
"""
|
||||
self.course.advanced_modules.extend(['annotatable', 'combinedopenended', 'peergrading'])
|
||||
templates = get_component_templates(self.course)
|
||||
button_names = [template['display_name'] for template in templates]
|
||||
self.assertIn('Advanced', button_names)
|
||||
self.assertEqual(len(templates[0]['templates']), 3)
|
||||
template_display_names = [template['display_name'] for template in templates[0]['templates']]
|
||||
self.assertEqual(template_display_names, ['Annotation', 'Open Response Assessment', 'Peer Grading Interface'])
|
||||
|
||||
|
||||
class TestXBlockInfo(ItemTest):
|
||||
"""
|
||||
|
||||
@@ -377,7 +377,10 @@ def choose_transcripts(request):
|
||||
if item.sub != html5_id: # update sub value
|
||||
item.sub = html5_id
|
||||
item.save_with_metadata(request.user)
|
||||
response = {'status': 'Success', 'subs': item.sub}
|
||||
response = {
|
||||
'status': 'Success',
|
||||
'subs': item.sub,
|
||||
}
|
||||
return JsonResponse(response)
|
||||
|
||||
|
||||
@@ -408,7 +411,10 @@ def replace_transcripts(request):
|
||||
|
||||
item.sub = youtube_id
|
||||
item.save_with_metadata(request.user)
|
||||
response = {'status': 'Success', 'subs': item.sub}
|
||||
response = {
|
||||
'status': 'Success',
|
||||
'subs': item.sub,
|
||||
}
|
||||
return JsonResponse(response)
|
||||
|
||||
|
||||
|
||||
@@ -100,7 +100,6 @@ def _course_team_user(request, course_key, email):
|
||||
}
|
||||
return JsonResponse(msg, 400)
|
||||
|
||||
|
||||
try:
|
||||
user = User.objects.get(email=email)
|
||||
except Exception:
|
||||
|
||||
@@ -11,6 +11,7 @@ from models.settings import course_grading
|
||||
from xmodule.fields import Date
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
|
||||
class CourseDetails(object):
|
||||
def __init__(self, org, course_id, run):
|
||||
# still need these for now b/c the client's screen shows these 3 fields
|
||||
|
||||
@@ -14,23 +14,24 @@ class CourseMetadata(object):
|
||||
# The list of fields that wouldn't be shown in Advanced Settings.
|
||||
# Should not be used directly. Instead the filtered_list method should be used if the field needs to be filtered
|
||||
# depending on the feature flag.
|
||||
FILTERED_LIST = ['xml_attributes',
|
||||
'start',
|
||||
'end',
|
||||
'enrollment_start',
|
||||
'enrollment_end',
|
||||
'tabs',
|
||||
'graceperiod',
|
||||
'checklists',
|
||||
'show_timezone',
|
||||
'format',
|
||||
'graded',
|
||||
'hide_from_toc',
|
||||
'pdf_textbooks',
|
||||
'user_partitions',
|
||||
'name', # from xblock
|
||||
'tags', # from xblock
|
||||
'visible_to_staff_only'
|
||||
FILTERED_LIST = [
|
||||
'xml_attributes',
|
||||
'start',
|
||||
'end',
|
||||
'enrollment_start',
|
||||
'enrollment_end',
|
||||
'tabs',
|
||||
'graceperiod',
|
||||
'checklists',
|
||||
'show_timezone',
|
||||
'format',
|
||||
'graded',
|
||||
'hide_from_toc',
|
||||
'pdf_textbooks',
|
||||
'user_partitions',
|
||||
'name', # from xblock
|
||||
'tags', # from xblock
|
||||
'visible_to_staff_only',
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -215,7 +215,8 @@ with open(CONFIG_ROOT / CONFIG_PREFIX + "auth.json") as auth_file:
|
||||
AUTH_TOKENS = json.load(auth_file)
|
||||
|
||||
############### XBlock filesystem field config ##########
|
||||
DJFS = AUTH_TOKENS.get('DJFS', None)
|
||||
if 'DJFS' in AUTH_TOKENS and AUTH_TOKENS['DJFS'] is not None:
|
||||
DJFS = AUTH_TOKENS['DJFS']
|
||||
|
||||
EMAIL_HOST_USER = AUTH_TOKENS.get('EMAIL_HOST_USER', EMAIL_HOST_USER)
|
||||
EMAIL_HOST_PASSWORD = AUTH_TOKENS.get('EMAIL_HOST_PASSWORD', EMAIL_HOST_PASSWORD)
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.mysql",
|
||||
"HOST": "localhost",
|
||||
"NAME": "test",
|
||||
"NAME": "edxtest",
|
||||
"PASSWORD": "",
|
||||
"PORT": "3306",
|
||||
"USER": "root"
|
||||
|
||||
@@ -292,7 +292,6 @@ MANAGERS = ADMINS
|
||||
|
||||
# Static content
|
||||
STATIC_URL = '/static/' + git.revision + "/"
|
||||
ADMIN_MEDIA_PREFIX = '/static/admin/'
|
||||
STATIC_ROOT = ENV_ROOT / "staticfiles" / git.revision
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
@@ -737,13 +736,17 @@ ADVANCED_COMPONENT_TYPES = [
|
||||
'done', # Lets students mark things as done. See https://github.com/pmitros/DoneXBlock
|
||||
'audio', # Embed an audio file. See https://github.com/pmitros/AudioXBlock
|
||||
'recommender', # Crowdsourced recommender. Prototype by dli&pmitros. Intended for roll-out in one place in one course.
|
||||
'profile', # Prototype user profile XBlock. Used to test XBlock parameter passing. See https://github.com/pmitros/ProfileXBlock
|
||||
'profile', # Prototype user profile XBlock. Used to test XBlock parameter passing. See https://github.com/pmitros/ProfileXBlock
|
||||
'split_test',
|
||||
'combinedopenended',
|
||||
'peergrading',
|
||||
'notes',
|
||||
]
|
||||
|
||||
# Adding components in this list will disable the creation of new problem for those
|
||||
# compoenents in studio. Existing problems will work fine and one can edit them in studio
|
||||
DEPRECATED_ADVANCED_COMPONENT_TYPES = []
|
||||
|
||||
# Specify xblocks that should be treated as advanced problems. Each entry is a tuple
|
||||
# specifying the xblock name and an optional YAML template to be used.
|
||||
ADVANCED_PROBLEM_TYPES = [
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Specific overrides to the base prod settings to make development easier.
|
||||
"""
|
||||
|
||||
from .aws import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
from .aws import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
|
||||
# Don't use S3 in devstack, fall back to filesystem
|
||||
del DEFAULT_FILE_STORAGE
|
||||
|
||||
@@ -69,9 +69,9 @@ STATICFILES_DIRS += [
|
||||
# If we don't add these settings, then Django templates that can't
|
||||
# find pipelined assets will raise a ValueError.
|
||||
# http://stackoverflow.com/questions/12816941/unit-testing-with-django-pipeline
|
||||
STATICFILES_STORAGE='pipeline.storage.NonPackagingPipelineStorage'
|
||||
STATICFILES_STORAGE = 'pipeline.storage.NonPackagingPipelineStorage'
|
||||
STATIC_URL = "/static/"
|
||||
PIPELINE_ENABLED=False
|
||||
PIPELINE_ENABLED = False
|
||||
|
||||
# Update module store settings per defaults for tests
|
||||
update_module_store_settings(
|
||||
|
||||
@@ -45,7 +45,8 @@
|
||||
'js/factories/settings',
|
||||
'js/factories/settings_advanced',
|
||||
'js/factories/settings_graders',
|
||||
'js/factories/textbooks'
|
||||
'js/factories/textbooks',
|
||||
'js/factories/xblock_validation'
|
||||
]),
|
||||
/**
|
||||
* By default all the configuration for optimization happens from the command
|
||||
|
||||
@@ -43,7 +43,7 @@ requirejs.config({
|
||||
"domReady": "xmodule_js/common_static/js/vendor/domReady",
|
||||
"URI": "xmodule_js/common_static/js/vendor/URI.min",
|
||||
|
||||
"mathjax": "//edx-static.s3.amazonaws.com/mathjax-MathJax-727332c/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured",
|
||||
"mathjax": "//cdn.mathjax.org/mathjax/2.2-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured",
|
||||
"youtube": "//www.youtube.com/player_api?noext",
|
||||
"tender": "//edxedge.tenderapp.com/tender_widget",
|
||||
|
||||
@@ -213,6 +213,7 @@ define([
|
||||
"js/spec/models/component_template_spec",
|
||||
"js/spec/models/explicit_url_spec",
|
||||
"js/spec/models/xblock_info_spec",
|
||||
"js/spec/models/xblock_validation_spec",
|
||||
|
||||
"js/spec/utils/drag_and_drop_spec",
|
||||
"js/spec/utils/handle_iframe_binding_spec",
|
||||
@@ -228,6 +229,7 @@ define([
|
||||
"js/spec/views/xblock_spec",
|
||||
"js/spec/views/xblock_editor_spec",
|
||||
"js/spec/views/xblock_string_field_editor_spec",
|
||||
"js/spec/views/xblock_validation_spec",
|
||||
|
||||
"js/spec/views/utils/view_utils_spec",
|
||||
|
||||
@@ -242,6 +244,10 @@ define([
|
||||
"js/spec/views/modals/edit_xblock_spec",
|
||||
"js/spec/views/modals/validation_error_modal_spec",
|
||||
|
||||
"js/spec/views/settings/main_spec",
|
||||
|
||||
"js/spec/factories/xblock_validation_spec",
|
||||
|
||||
"js/spec/xblock/cms.runtime.v1_spec",
|
||||
|
||||
# these tests are run separately in the cms-squire suite, due to process
|
||||
|
||||
@@ -38,7 +38,7 @@ requirejs.config({
|
||||
"domReady": "xmodule_js/common_static/js/vendor/domReady",
|
||||
"URI": "xmodule_js/common_static/js/vendor/URI.min",
|
||||
|
||||
"mathjax": "//edx-static.s3.amazonaws.com/mathjax-MathJax-727332c/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured",
|
||||
"mathjax": "//cdn.mathjax.org/mathjax/2.2-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured",
|
||||
"youtube": "//www.youtube.com/player_api?noext",
|
||||
"tender": "//edxedge.tenderapp.com/tender_widget.js"
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 117 KiB |
BIN
cms/static/fonts/OpenSans/OpenSans-ExtraBold-webfont.ttf
Executable file
BIN
cms/static/fonts/OpenSans/OpenSans-ExtraBoldItalic-webfont.ttf
Executable file
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 117 B After Width: | Height: | Size: 117 B |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 954 B After Width: | Height: | Size: 954 B |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 633 B After Width: | Height: | Size: 633 B |
|
Before Width: | Height: | Size: 737 B After Width: | Height: | Size: 737 B |
|
Before Width: | Height: | Size: 581 B After Width: | Height: | Size: 581 B |
|
Before Width: | Height: | Size: 797 B After Width: | Height: | Size: 797 B |
|
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 234 B |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |