Merge branch 'feature/christina/metadata-ui' of github.com:edx/edx-platform into feature/christina/metadata-ui
This commit is contained in:
@@ -11,7 +11,8 @@ Feature: Advanced (manual) course policy
|
||||
Given I am on the Advanced Course Settings page in Studio
|
||||
Then the settings are alphabetized
|
||||
|
||||
@skip-phantom
|
||||
# Skipped because Ubuntu ChromeDriver cannot click notification "Cancel"
|
||||
@skip
|
||||
Scenario: Test cancel editing key value
|
||||
Given I am on the Advanced Course Settings page in Studio
|
||||
When I edit the value of a policy key
|
||||
@@ -20,7 +21,8 @@ Feature: Advanced (manual) course policy
|
||||
And I reload the page
|
||||
Then the policy key value is unchanged
|
||||
|
||||
@skip-phantom
|
||||
# Skipped because Ubuntu ChromeDriver cannot click notification "Save"
|
||||
@skip
|
||||
Scenario: Test editing key value
|
||||
Given I am on the Advanced Course Settings page in Studio
|
||||
When I edit the value of a policy key and save
|
||||
@@ -28,7 +30,8 @@ Feature: Advanced (manual) course policy
|
||||
And I reload the page
|
||||
Then the policy key value is changed
|
||||
|
||||
@skip-phantom
|
||||
# Skipped because Ubuntu ChromeDriver cannot edit CodeMirror input
|
||||
@skip
|
||||
Scenario: Test how multi-line input appears
|
||||
Given I am on the Advanced Course Settings page in Studio
|
||||
When I create a JSON object as a value
|
||||
@@ -36,7 +39,8 @@ Feature: Advanced (manual) course policy
|
||||
And I reload the page
|
||||
Then it is displayed as formatted
|
||||
|
||||
@skip-phantom
|
||||
# Skipped because Ubuntu ChromeDriver cannot edit CodeMirror input
|
||||
@skip
|
||||
Scenario: Test automatic quoting of non-JSON values
|
||||
Given I am on the Advanced Course Settings page in Studio
|
||||
When I create a non-JSON value not in quotes
|
||||
|
||||
@@ -10,8 +10,6 @@ Feature: Course checklists
|
||||
Then I can check and uncheck tasks in a checklist
|
||||
And They are correctly selected after I reload the page
|
||||
|
||||
@skip-phantom
|
||||
@skip-firefox
|
||||
Scenario: A task can link to a location within Studio
|
||||
Given I have opened Checklists
|
||||
When I select a link to the course outline
|
||||
@@ -19,8 +17,6 @@ Feature: Course checklists
|
||||
And I press the browser back button
|
||||
Then I am brought back to the course outline in the correct state
|
||||
|
||||
@skip-phantom
|
||||
@skip-firefox
|
||||
Scenario: A task can link to a location outside Studio
|
||||
Given I have opened Checklists
|
||||
When I select a link to help page
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
Feature: Course Settings
|
||||
As a course author, I want to be able to configure my course settings.
|
||||
|
||||
@skip-phantom
|
||||
Scenario: User can set course dates
|
||||
Given I have opened a new course in Studio
|
||||
When I select Schedule and Details
|
||||
And I set course dates
|
||||
Then I see the set dates on refresh
|
||||
|
||||
@skip-phantom
|
||||
Scenario: User can clear previously set course dates (except start date)
|
||||
Given I have set course dates
|
||||
And I clear all the dates except start
|
||||
Then I see cleared dates on refresh
|
||||
|
||||
@skip-phantom
|
||||
Scenario: User cannot clear the course start date
|
||||
Given I have set course dates
|
||||
And I clear the course start date
|
||||
|
||||
@@ -3,7 +3,6 @@ Feature: Create Section
|
||||
As a course author
|
||||
I want to create and edit sections
|
||||
|
||||
@skip-phantom
|
||||
Scenario: Add a new section to a course
|
||||
Given I have opened a new course in Studio
|
||||
When I click the New Section link
|
||||
@@ -27,7 +26,8 @@ Feature: Create Section
|
||||
And I save a new section release date
|
||||
Then the section release date is updated
|
||||
|
||||
@skip-phantom
|
||||
# Skipped because Ubuntu ChromeDriver hangs on alert
|
||||
@skip
|
||||
Scenario: Delete section
|
||||
Given I have opened a new course in Studio
|
||||
And I have added a new section
|
||||
|
||||
@@ -18,10 +18,7 @@ def i_fill_in_the_registration_form(step):
|
||||
@step('I press the Create My Account button on the registration form$')
|
||||
def i_press_the_button_on_the_registration_form(step):
|
||||
submit_css = 'form#register_form button#submit'
|
||||
# Workaround for click not working on ubuntu
|
||||
# for some unknown reason.
|
||||
e = world.css_find(submit_css)
|
||||
e.type(' ')
|
||||
world.css_click(submit_css)
|
||||
|
||||
|
||||
@step('I should see be on the studio home page$')
|
||||
|
||||
@@ -14,7 +14,6 @@ Feature: Overview Toggle Section
|
||||
When I navigate to the course overview page
|
||||
Then I do not see the "Collapse All Sections" link
|
||||
|
||||
@skip-phantom
|
||||
Scenario: Collapse link appears after creating first section of a course
|
||||
Given I have a course with no sections
|
||||
When I navigate to the course overview page
|
||||
@@ -22,7 +21,8 @@ Feature: Overview Toggle Section
|
||||
Then I see the "Collapse All Sections" link
|
||||
And all sections are expanded
|
||||
|
||||
@skip-phantom
|
||||
# Skipped because Ubuntu ChromeDriver hangs on alert
|
||||
@skip
|
||||
Scenario: Collapse link is not removed after last section of a course is deleted
|
||||
Given I have a course with 1 section
|
||||
And I navigate to the course overview page
|
||||
|
||||
@@ -3,14 +3,12 @@ Feature: Create Subsection
|
||||
As a course author
|
||||
I want to create and edit subsections
|
||||
|
||||
@skip-phantom
|
||||
Scenario: Add a new subsection to a section
|
||||
Given I have opened a new course section in Studio
|
||||
When I click the New Subsection link
|
||||
And I enter the subsection name and click save
|
||||
Then I see my subsection on the Courseware page
|
||||
|
||||
@skip-phantom
|
||||
Scenario: Add a new subsection (with a name containing a quote) to a section (bug #216)
|
||||
Given I have opened a new course section in Studio
|
||||
When I click the New Subsection link
|
||||
@@ -27,7 +25,6 @@ Feature: Create Subsection
|
||||
And I reload the page
|
||||
Then I see it marked as Homework
|
||||
|
||||
@skip-phantom
|
||||
Scenario: Set a due date in a different year (bug #256)
|
||||
Given I have opened a new subsection in Studio
|
||||
And I have set a release date and due date in different years
|
||||
@@ -35,7 +32,8 @@ Feature: Create Subsection
|
||||
And I reload the page
|
||||
Then I see the correct dates
|
||||
|
||||
@skip-phantom
|
||||
# Skipped because Ubuntu ChromeDriver hangs on alert
|
||||
@skip
|
||||
Scenario: Delete a subsection
|
||||
Given I have opened a new course section in Studio
|
||||
And I have added a new subsection
|
||||
|
||||
@@ -63,14 +63,6 @@ def test_have_set_dates_in_different_years(step):
|
||||
set_date_and_time('input#due_date', '01/02/2012', 'input#due_time', '04:00')
|
||||
|
||||
|
||||
@step('I see the correct dates$')
|
||||
def i_see_the_correct_dates(step):
|
||||
assert_equal('12/25/2011', world.css_find('input#start_date').first.value)
|
||||
assert_equal('03:00', world.css_find('input#start_time').first.value)
|
||||
assert_equal('01/02/2012', world.css_find('input#due_date').first.value)
|
||||
assert_equal('04:00', world.css_find('input#due_time').first.value)
|
||||
|
||||
|
||||
@step('I mark it as Homework$')
|
||||
def i_mark_it_as_homework(step):
|
||||
world.css_click('a.menu-toggle')
|
||||
@@ -101,8 +93,20 @@ def the_subsection_does_not_exist(step):
|
||||
assert world.browser.is_element_not_present_by_css(css)
|
||||
|
||||
|
||||
@step('I see the correct dates$')
|
||||
def i_see_the_correct_dates(step):
|
||||
assert_equal('12/25/2011', get_date('input#start_date'))
|
||||
assert_equal('03:00', get_date('input#start_time'))
|
||||
assert_equal('01/02/2012', get_date('input#due_date'))
|
||||
assert_equal('04:00', get_date('input#due_time'))
|
||||
|
||||
|
||||
############ HELPER METHODS ###################
|
||||
|
||||
def get_date(css):
|
||||
return world.css_find(css).first.value.strip()
|
||||
|
||||
|
||||
def save_subsection_name(name):
|
||||
name_css = 'input.new-subsection-name-input'
|
||||
save_css = 'input.new-subsection-name-save'
|
||||
|
||||
@@ -74,7 +74,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.client.login(username=uname, password=password)
|
||||
|
||||
def check_edit_unit(self, test_course_name):
|
||||
import_from_xml(modulestore(), 'common/test/data/', [test_course_name])
|
||||
import_from_xml(modulestore('direct'), 'common/test/data/', [test_course_name])
|
||||
|
||||
for descriptor in modulestore().get_items(Location(None, None, 'vertical', None, None)):
|
||||
print "Checking ", descriptor.location.url()
|
||||
@@ -101,7 +101,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
Unfortunately, None = published for the revision field, so get_items() would return
|
||||
both draft and non-draft copies.
|
||||
'''
|
||||
store = modulestore()
|
||||
store = modulestore('direct')
|
||||
draft_store = modulestore('draft')
|
||||
import_from_xml(store, 'common/test/data/', ['simple'])
|
||||
|
||||
@@ -128,7 +128,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
module as 'own-metadata' when publishing. Also verifies the metadata inheritance is
|
||||
properly computed
|
||||
'''
|
||||
store = modulestore()
|
||||
store = modulestore('direct')
|
||||
draft_store = modulestore('draft')
|
||||
import_from_xml(store, 'common/test/data/', ['simple'])
|
||||
|
||||
@@ -186,7 +186,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.assertEqual(html_module.lms.graceperiod, new_graceperiod)
|
||||
|
||||
def test_get_depth_with_drafts(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['simple'])
|
||||
import_from_xml(modulestore('direct'), 'common/test/data/', ['simple'])
|
||||
|
||||
course = modulestore('draft').get_item(
|
||||
Location(['i4x', 'edX', 'simple', 'course', '2012_Fall', None]),
|
||||
@@ -221,17 +221,17 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.assertEqual(num_drafts, 1)
|
||||
|
||||
def test_import_textbook_as_content_element(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
|
||||
module_store = modulestore('direct')
|
||||
import_from_xml(module_store, 'common/test/data/', ['full'])
|
||||
|
||||
course = module_store.get_item(Location(['i4x', 'edX', 'full', 'course', '6.002_Spring_2012', None]))
|
||||
|
||||
self.assertGreater(len(course.textbooks), 0)
|
||||
|
||||
def test_static_tab_reordering(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
|
||||
module_store = modulestore('direct')
|
||||
import_from_xml(module_store, 'common/test/data/', ['full'])
|
||||
|
||||
course = module_store.get_item(Location(['i4x', 'edX', 'full', 'course', '6.002_Spring_2012', None]))
|
||||
|
||||
# reverse the ordering
|
||||
@@ -253,10 +253,8 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.assertEqual(reverse_tabs, course_tabs)
|
||||
|
||||
def test_import_polls(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
|
||||
module_store = modulestore('direct')
|
||||
found = False
|
||||
import_from_xml(module_store, 'common/test/data/', ['full'])
|
||||
|
||||
items = module_store.get_items(['i4x', 'edX', 'full', 'poll_question', None, None])
|
||||
found = len(items) > 0
|
||||
@@ -270,9 +268,8 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.assertGreater(err_cnt, 0)
|
||||
|
||||
def test_delete(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
|
||||
direct_store = modulestore('direct')
|
||||
import_from_xml(direct_store, 'common/test/data/', ['full'])
|
||||
|
||||
sequential = direct_store.get_item(Location(['i4x', 'edX', 'full', 'sequential', 'Administrivia_and_Circuit_Elements', None]))
|
||||
|
||||
@@ -306,8 +303,9 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
This test case verifies that a course can use specialized override for about data, e.g. /about/Fall_2012/effort.html
|
||||
while there is a base definition in /about/effort.html
|
||||
'''
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
module_store = modulestore('direct')
|
||||
import_from_xml(module_store, 'common/test/data/', ['full'])
|
||||
|
||||
effort = module_store.get_item(Location(['i4x', 'edX', 'full', 'about', 'effort', None]))
|
||||
self.assertEqual(effort.data, '6 hours')
|
||||
|
||||
@@ -316,9 +314,8 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.assertEqual(effort.data, 'TBD')
|
||||
|
||||
def test_remove_hide_progress_tab(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
|
||||
module_store = modulestore('direct')
|
||||
import_from_xml(module_store, 'common/test/data/', ['full'])
|
||||
|
||||
source_location = CourseDescriptor.id_to_location('edX/full/6.002_Spring_2012')
|
||||
course = module_store.get_item(source_location)
|
||||
@@ -333,14 +330,14 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
'display_name': 'Robot Super Course',
|
||||
}
|
||||
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
module_store = modulestore('direct')
|
||||
import_from_xml(module_store, 'common/test/data/', ['full'])
|
||||
|
||||
resp = self.client.post(reverse('create_new_course'), course_data)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = parse_json(resp)
|
||||
self.assertEqual(data['id'], 'i4x://MITx/999/course/Robot_Super_Course')
|
||||
|
||||
module_store = modulestore('direct')
|
||||
content_store = contentstore()
|
||||
|
||||
source_location = CourseDescriptor.id_to_location('edX/full/6.002_Spring_2012')
|
||||
@@ -365,9 +362,9 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
|
||||
def test_delete_course(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
|
||||
module_store = modulestore('direct')
|
||||
import_from_xml(module_store, 'common/test/data/', ['full'])
|
||||
|
||||
content_store = contentstore()
|
||||
|
||||
location = CourseDescriptor.id_to_location('edX/full/6.002_Spring_2012')
|
||||
@@ -523,8 +520,9 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.assertContains(resp, '/c4x/edX/full/asset/handouts_schematic_tutorial.pdf')
|
||||
|
||||
def test_prefetch_children(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
module_store = modulestore('direct')
|
||||
import_from_xml(module_store, 'common/test/data/', ['full'])
|
||||
|
||||
location = CourseDescriptor.id_to_location('edX/full/6.002_Spring_2012')
|
||||
|
||||
wrapper = MongoCollectionFindWrapper(module_store.collection.find)
|
||||
@@ -736,7 +734,7 @@ class ContentStoreTest(ModuleStoreTestCase):
|
||||
Import and walk through some common URL endpoints. This just verifies non-500 and no other
|
||||
correct behavior, so it is not a deep test
|
||||
"""
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['simple'])
|
||||
import_from_xml(modulestore('direct'), 'common/test/data/', ['simple'])
|
||||
loc = Location(['i4x', 'edX', 'simple', 'course', '2012_Fall', None])
|
||||
resp = self.client.get(reverse('course_index',
|
||||
kwargs={'org': loc.org,
|
||||
@@ -838,9 +836,11 @@ class ContentStoreTest(ModuleStoreTestCase):
|
||||
json.dumps({'id': del_loc.url()}), "application/json")
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
|
||||
def test_import_metadata_with_attempts_empty_string(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['simple'])
|
||||
module_store = modulestore('direct')
|
||||
import_from_xml(module_store, 'common/test/data/', ['simple'])
|
||||
|
||||
did_load_item = False
|
||||
try:
|
||||
module_store.get_item(Location(['i4x', 'edX', 'simple', 'problem', 'ps01-simple', None]))
|
||||
@@ -852,8 +852,9 @@ class ContentStoreTest(ModuleStoreTestCase):
|
||||
self.assertTrue(did_load_item)
|
||||
|
||||
def test_forum_id_generation(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
module_store = modulestore('direct')
|
||||
import_from_xml(module_store, 'common/test/data/', ['full'])
|
||||
|
||||
new_component_location = Location('i4x', 'edX', 'full', 'discussion', 'new_component')
|
||||
source_template_location = Location('i4x', 'edx', 'templates', 'discussion', 'Discussion_Tag')
|
||||
|
||||
@@ -865,9 +866,8 @@ class ContentStoreTest(ModuleStoreTestCase):
|
||||
self.assertNotEquals(new_discussion_item.discussion_id, '$$GUID$$')
|
||||
|
||||
def test_update_modulestore_signal_did_fire(self):
|
||||
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
module_store = modulestore('direct')
|
||||
import_from_xml(module_store, 'common/test/data/', ['full'])
|
||||
|
||||
try:
|
||||
module_store.modulestore_update_signal = Signal(providing_args=['modulestore', 'course_id', 'location'])
|
||||
@@ -891,9 +891,9 @@ class ContentStoreTest(ModuleStoreTestCase):
|
||||
self.assertTrue(self.got_signal)
|
||||
|
||||
def test_metadata_inheritance(self):
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
|
||||
module_store = modulestore('direct')
|
||||
import_from_xml(module_store, 'common/test/data/', ['full'])
|
||||
|
||||
course = module_store.get_item(Location(['i4x', 'edX', 'full', 'course', '6.002_Spring_2012', None]))
|
||||
|
||||
verticals = module_store.get_items(['i4x', 'edX', 'full', 'vertical', None, None])
|
||||
|
||||
@@ -17,7 +17,6 @@ from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
from models.settings.course_metadata import CourseMetadata
|
||||
from xmodule.modulestore.xml_importer import import_from_xml
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.fields import Date
|
||||
|
||||
|
||||
@@ -256,7 +255,7 @@ class CourseMetadataEditingTest(CourseTestCase):
|
||||
def setUp(self):
|
||||
CourseTestCase.setUp(self)
|
||||
# add in the full class too
|
||||
import_from_xml(modulestore(), 'common/test/data/', ['full'])
|
||||
import_from_xml(get_modulestore(self.course_location), 'common/test/data/', ['full'])
|
||||
self.fullcourse_location = Location(['i4x', 'edX', 'full', 'course', '6.002_Spring_2012', None])
|
||||
|
||||
def test_fetch_initial_fields(self):
|
||||
|
||||
29
cms/djangoapps/contentstore/tests/test_item.py
Normal file
29
cms/djangoapps/contentstore/tests/test_item.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from contentstore.utils import get_modulestore, get_url_reverse
|
||||
from contentstore.tests.test_course_settings import CourseTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
|
||||
class DeleteItem(CourseTestCase):
|
||||
def setUp(self):
|
||||
""" Creates the test course with a static page in it. """
|
||||
super(DeleteItem, self).setUp()
|
||||
self.course = CourseFactory.create(org='mitX', number='333', display_name='Dummy Course')
|
||||
|
||||
def testDeleteStaticPage(self):
|
||||
# Add static tab
|
||||
data = {
|
||||
'parent_location': 'i4x://mitX/333/course/Dummy_Course',
|
||||
'template': 'i4x://edx/templates/static_tab/Empty'
|
||||
}
|
||||
|
||||
resp = self.client.post(reverse('clone_item'), data)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
# Now delete it. There was a bug that the delete was failing (static tabs do not exist in draft modulestore).
|
||||
resp = self.client.post(reverse('delete_item'), resp.content, "application/json")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
""" Tests for utils. """
|
||||
from contentstore import utils
|
||||
import mock
|
||||
import collections
|
||||
import copy
|
||||
from django.test import TestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
@@ -70,3 +72,79 @@ class UrlReverseTestCase(ModuleStoreTestCase):
|
||||
'https://edge.edx.org/courses/edX/edX101/How_to_Create_an_edX_Course/about',
|
||||
utils.get_url_reverse('https://edge.edx.org/courses/edX/edX101/How_to_Create_an_edX_Course/about', course)
|
||||
)
|
||||
|
||||
|
||||
class ExtraPanelTabTestCase(TestCase):
|
||||
""" Tests adding and removing extra course tabs. """
|
||||
|
||||
def get_tab_type_dicts(self, tab_types):
|
||||
""" Returns an array of tab dictionaries. """
|
||||
if tab_types:
|
||||
return [{'tab_type': tab_type} for tab_type in tab_types.split(',')]
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_course_with_tabs(self, tabs=[]):
|
||||
""" Returns a mock course object with a tabs attribute. """
|
||||
course = collections.namedtuple('MockCourse', ['tabs'])
|
||||
if isinstance(tabs, basestring):
|
||||
course.tabs = self.get_tab_type_dicts(tabs)
|
||||
else:
|
||||
course.tabs = tabs
|
||||
return course
|
||||
|
||||
def test_add_extra_panel_tab(self):
|
||||
""" Tests if a tab can be added to a course tab list. """
|
||||
for tab_type in utils.EXTRA_TAB_PANELS.keys():
|
||||
tab = utils.EXTRA_TAB_PANELS.get(tab_type)
|
||||
|
||||
# test adding with changed = True
|
||||
for tab_setup in ['', 'x', 'x,y,z']:
|
||||
course = self.get_course_with_tabs(tab_setup)
|
||||
expected_tabs = copy.copy(course.tabs)
|
||||
expected_tabs.append(tab)
|
||||
changed, actual_tabs = utils.add_extra_panel_tab(tab_type, course)
|
||||
self.assertTrue(changed)
|
||||
self.assertEqual(actual_tabs, expected_tabs)
|
||||
|
||||
# test adding with changed = False
|
||||
tab_test_setup = [
|
||||
[tab],
|
||||
[tab, self.get_tab_type_dicts('x,y,z')],
|
||||
[self.get_tab_type_dicts('x,y'), tab, self.get_tab_type_dicts('z')],
|
||||
[self.get_tab_type_dicts('x,y,z'), tab]]
|
||||
|
||||
for tab_setup in tab_test_setup:
|
||||
course = self.get_course_with_tabs(tab_setup)
|
||||
expected_tabs = copy.copy(course.tabs)
|
||||
changed, actual_tabs = utils.add_extra_panel_tab(tab_type, course)
|
||||
self.assertFalse(changed)
|
||||
self.assertEqual(actual_tabs, expected_tabs)
|
||||
|
||||
def test_remove_extra_panel_tab(self):
|
||||
""" Tests if a tab can be removed from a course tab list. """
|
||||
for tab_type in utils.EXTRA_TAB_PANELS.keys():
|
||||
tab = utils.EXTRA_TAB_PANELS.get(tab_type)
|
||||
|
||||
# test removing with changed = True
|
||||
tab_test_setup = [
|
||||
[tab],
|
||||
[tab, self.get_tab_type_dicts('x,y,z')],
|
||||
[self.get_tab_type_dicts('x,y'), tab, self.get_tab_type_dicts('z')],
|
||||
[self.get_tab_type_dicts('x,y,z'), tab]]
|
||||
|
||||
for tab_setup in tab_test_setup:
|
||||
course = self.get_course_with_tabs(tab_setup)
|
||||
expected_tabs = [t for t in course.tabs if t != utils.EXTRA_TAB_PANELS.get(tab_type)]
|
||||
changed, actual_tabs = utils.remove_extra_panel_tab(tab_type, course)
|
||||
self.assertTrue(changed)
|
||||
self.assertEqual(actual_tabs, expected_tabs)
|
||||
|
||||
# test removing with changed = False
|
||||
for tab_setup in ['', 'x', 'x,y,z']:
|
||||
course = self.get_course_with_tabs(tab_setup)
|
||||
expected_tabs = copy.copy(course.tabs)
|
||||
changed, actual_tabs = utils.remove_extra_panel_tab(tab_type, course)
|
||||
self.assertFalse(changed)
|
||||
self.assertEqual(actual_tabs, expected_tabs)
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ DIRECT_ONLY_CATEGORIES = ['course', 'chapter', 'sequential', 'about', 'static_ta
|
||||
|
||||
#In order to instantiate an open ended tab automatically, need to have this data
|
||||
OPEN_ENDED_PANEL = {"name": "Open Ended Panel", "type": "open_ended"}
|
||||
NOTES_PANEL = {"name": "My Notes", "type": "notes"}
|
||||
EXTRA_TAB_PANELS = dict([(p['type'], p) for p in [OPEN_ENDED_PANEL, NOTES_PANEL]])
|
||||
|
||||
|
||||
def get_modulestore(location):
|
||||
@@ -192,9 +194,10 @@ class CoursePageNames:
|
||||
Checklists = "checklists"
|
||||
|
||||
|
||||
def add_open_ended_panel_tab(course):
|
||||
def add_extra_panel_tab(tab_type, course):
|
||||
"""
|
||||
Used to add the open ended panel tab to a course if it does not exist.
|
||||
Used to add the panel tab to a course if it does not exist.
|
||||
@param tab_type: A string representing the tab type.
|
||||
@param course: A course object from the modulestore.
|
||||
@return: Boolean indicating whether or not a tab was added and a list of tabs for the course.
|
||||
"""
|
||||
@@ -202,16 +205,19 @@ def add_open_ended_panel_tab(course):
|
||||
course_tabs = copy.copy(course.tabs)
|
||||
changed = False
|
||||
#Check to see if open ended panel is defined in the course
|
||||
if OPEN_ENDED_PANEL not in course_tabs:
|
||||
|
||||
tab_panel = EXTRA_TAB_PANELS.get(tab_type)
|
||||
if tab_panel not in course_tabs:
|
||||
#Add panel to the tabs if it is not defined
|
||||
course_tabs.append(OPEN_ENDED_PANEL)
|
||||
course_tabs.append(tab_panel)
|
||||
changed = True
|
||||
return changed, course_tabs
|
||||
|
||||
|
||||
def remove_open_ended_panel_tab(course):
|
||||
def remove_extra_panel_tab(tab_type, course):
|
||||
"""
|
||||
Used to remove the open ended panel tab from a course if it exists.
|
||||
Used to remove the panel tab from a course if it exists.
|
||||
@param tab_type: A string representing the tab type.
|
||||
@param course: A course object from the modulestore.
|
||||
@return: Boolean indicating whether or not a tab was added and a list of tabs for the course.
|
||||
"""
|
||||
@@ -219,8 +225,10 @@ def remove_open_ended_panel_tab(course):
|
||||
course_tabs = copy.copy(course.tabs)
|
||||
changed = False
|
||||
#Check to see if open ended panel is defined in the course
|
||||
if OPEN_ENDED_PANEL in course_tabs:
|
||||
|
||||
tab_panel = EXTRA_TAB_PANELS.get(tab_type)
|
||||
if tab_panel in course_tabs:
|
||||
#Add panel to the tabs if it is not defined
|
||||
course_tabs = [ct for ct in course_tabs if ct != OPEN_ENDED_PANEL]
|
||||
course_tabs = [ct for ct in course_tabs if ct != tab_panel]
|
||||
changed = True
|
||||
return changed, course_tabs
|
||||
|
||||
@@ -41,7 +41,8 @@ log = logging.getLogger(__name__)
|
||||
COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video']
|
||||
|
||||
OPEN_ENDED_COMPONENT_TYPES = ["combinedopenended", "peergrading"]
|
||||
ADVANCED_COMPONENT_TYPES = ['annotatable', 'word_cloud'] + OPEN_ENDED_COMPONENT_TYPES
|
||||
NOTE_COMPONENT_TYPES = ['notes']
|
||||
ADVANCED_COMPONENT_TYPES = ['annotatable' + 'word_cloud'] + OPEN_ENDED_COMPONENT_TYPES + NOTE_COMPONENT_TYPES
|
||||
ADVANCED_COMPONENT_CATEGORY = 'advanced'
|
||||
ADVANCED_COMPONENT_POLICY_KEY = 'advanced_modules'
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ from xmodule.modulestore import Location
|
||||
from contentstore.course_info_model \
|
||||
import get_course_updates, update_course_updates, delete_course_update
|
||||
from contentstore.utils \
|
||||
import get_lms_link_for_item, add_open_ended_panel_tab, \
|
||||
remove_open_ended_panel_tab
|
||||
import get_lms_link_for_item, add_extra_panel_tab, \
|
||||
remove_extra_panel_tab
|
||||
from models.settings.course_details \
|
||||
import CourseDetails, CourseSettingsEncoder
|
||||
from models.settings.course_grading import CourseGradingModel
|
||||
@@ -32,7 +32,8 @@ from util.json_request import expect_json
|
||||
from .access import has_access, get_location_and_verify_access
|
||||
from .requests import get_request_method
|
||||
from .tabs import initialize_course_tabs
|
||||
from .component import OPEN_ENDED_COMPONENT_TYPES, ADVANCED_COMPONENT_POLICY_KEY
|
||||
from .component import OPEN_ENDED_COMPONENT_TYPES, \
|
||||
NOTE_COMPONENT_TYPES, ADVANCED_COMPONENT_POLICY_KEY
|
||||
|
||||
__all__ = ['course_index', 'create_new_course', 'course_info',
|
||||
'course_info_updates', 'get_course_settings',
|
||||
@@ -352,38 +353,52 @@ def course_advanced_updates(request, org, course, name):
|
||||
request_body = json.loads(request.body)
|
||||
# Whether or not to filter the tabs key out of the settings metadata
|
||||
filter_tabs = True
|
||||
# Check to see if the user instantiated any advanced components.
|
||||
# This is a hack to add the open ended panel tab
|
||||
# to a course automatically if the user has indicated that they want
|
||||
# to edit the combinedopenended or peergrading
|
||||
# module, and to remove it if they have removed the open ended elements.
|
||||
|
||||
#Check to see if the user instantiated any advanced components. This is a hack
|
||||
#that does the following :
|
||||
# 1) adds/removes the open ended panel tab to a course automatically if the user
|
||||
# has indicated that they want to edit the combinedopendended or peergrading module
|
||||
# 2) adds/removes the notes panel tab to a course automatically if the user has
|
||||
# indicated that they want the notes module enabled in their course
|
||||
# TODO refactor the above into distinct advanced policy settings
|
||||
if ADVANCED_COMPONENT_POLICY_KEY in request_body:
|
||||
# Check to see if the user instantiated any open ended components
|
||||
found_oe_type = False
|
||||
# Get the course so that we can scrape current tabs
|
||||
#Get the course so that we can scrape current tabs
|
||||
course_module = modulestore().get_item(location)
|
||||
for oe_type in OPEN_ENDED_COMPONENT_TYPES:
|
||||
if oe_type in request_body[ADVANCED_COMPONENT_POLICY_KEY]:
|
||||
# Add an open ended tab to the course if needed
|
||||
changed, new_tabs = add_open_ended_panel_tab(course_module)
|
||||
# If a tab has been added to the course, then send the
|
||||
# metadata along to CourseMetadata.update_from_json
|
||||
|
||||
#Maps tab types to components
|
||||
tab_component_map = {
|
||||
'open_ended': OPEN_ENDED_COMPONENT_TYPES,
|
||||
'notes': NOTE_COMPONENT_TYPES,
|
||||
}
|
||||
|
||||
#Check to see if the user instantiated any notes or open ended components
|
||||
for tab_type in tab_component_map.keys():
|
||||
component_types = tab_component_map.get(tab_type)
|
||||
found_ac_type = False
|
||||
for ac_type in component_types:
|
||||
if ac_type in request_body[ADVANCED_COMPONENT_POLICY_KEY]:
|
||||
#Add tab to the course if needed
|
||||
changed, new_tabs = add_extra_panel_tab(tab_type, course_module)
|
||||
#If a tab has been added to the course, then send the metadata along to CourseMetadata.update_from_json
|
||||
if changed:
|
||||
course_module.tabs = new_tabs
|
||||
request_body.update({'tabs': new_tabs})
|
||||
#Indicate that tabs should not be filtered out of the metadata
|
||||
filter_tabs = False
|
||||
#Set this flag to avoid the tab removal code below.
|
||||
found_ac_type = True
|
||||
break
|
||||
#If we did not find a module type in the advanced settings,
|
||||
# we may need to remove the tab from the course.
|
||||
if not found_ac_type:
|
||||
#Remove tab from the course if needed
|
||||
changed, new_tabs = remove_extra_panel_tab(tab_type, course_module)
|
||||
if changed:
|
||||
course_module.tabs = new_tabs
|
||||
request_body.update({'tabs': new_tabs})
|
||||
# Indicate that tabs should not be filtered out of the metadata
|
||||
#Indicate that tabs should *not* be filtered out of the metadata
|
||||
filter_tabs = False
|
||||
# Set this flag to avoid the open ended tab removal code below.
|
||||
found_oe_type = True
|
||||
break
|
||||
# If we did not find an open ended module type in the advanced settings,
|
||||
# we may need to remove the open ended tab from the course.
|
||||
if not found_oe_type:
|
||||
# Remove open ended tab to the course if needed
|
||||
changed, new_tabs = remove_open_ended_panel_tab(course_module)
|
||||
if changed:
|
||||
request_body.update({'tabs': new_tabs})
|
||||
# Indicate that tabs should not be filtered out of the metadata
|
||||
filter_tabs = False
|
||||
|
||||
response_json = json.dumps(CourseMetadata.update_from_json(location,
|
||||
request_body,
|
||||
filter_tabs=filter_tabs))
|
||||
|
||||
@@ -113,7 +113,7 @@ def delete_item(request):
|
||||
delete_children = request.POST.get('delete_children', False)
|
||||
delete_all_versions = request.POST.get('delete_all_versions', False)
|
||||
|
||||
store = modulestore()
|
||||
store = get_modulestore(item_location)
|
||||
|
||||
item = store.get_item(item_location)
|
||||
|
||||
|
||||
@@ -8,27 +8,41 @@ from .test import *
|
||||
# otherwise the browser will not render the pages correctly
|
||||
DEBUG = True
|
||||
|
||||
# Show the courses that are in the data directory
|
||||
COURSES_ROOT = ENV_ROOT / "data"
|
||||
DATA_DIR = COURSES_ROOT
|
||||
# MODULESTORE = {
|
||||
# 'default': {
|
||||
# 'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore',
|
||||
# 'OPTIONS': {
|
||||
# 'data_dir': DATA_DIR,
|
||||
# 'default_class': 'xmodule.hidden_module.HiddenDescriptor',
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# Disable warnings for acceptance tests, to make the logs readable
|
||||
import logging
|
||||
logging.disable(logging.ERROR)
|
||||
|
||||
MODULESTORE_OPTIONS = {
|
||||
'default_class': 'xmodule.raw_module.RawDescriptor',
|
||||
'host': 'localhost',
|
||||
'db': 'test_xmodule',
|
||||
'collection': 'acceptance_modulestore',
|
||||
'fs_root': TEST_ROOT / "data",
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string',
|
||||
}
|
||||
|
||||
MODULESTORE = {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
|
||||
'OPTIONS': MODULESTORE_OPTIONS
|
||||
},
|
||||
'direct': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
|
||||
'OPTIONS': MODULESTORE_OPTIONS
|
||||
},
|
||||
'draft': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore',
|
||||
'OPTIONS': MODULESTORE_OPTIONS
|
||||
}
|
||||
}
|
||||
# Set this up so that rake lms[acceptance] and running the
|
||||
# harvest command both use the same (test) database
|
||||
# which they can flush without messing up your dev db
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': ENV_ROOT / "db" / "test_mitx.db",
|
||||
'TEST_NAME': ENV_ROOT / "db" / "test_mitx.db",
|
||||
'NAME': TEST_ROOT / "db" / "test_mitx.db",
|
||||
'TEST_NAME': TEST_ROOT / "db" / "test_mitx.db",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,48 @@ EMAIL_BACKEND = 'django_ses.SESBackend'
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
|
||||
|
||||
###################################### CELERY ################################
|
||||
|
||||
# Don't use a connection pool, since connections are dropped by ELB.
|
||||
BROKER_POOL_LIMIT = 0
|
||||
BROKER_CONNECTION_TIMEOUT = 1
|
||||
|
||||
# For the Result Store, use the django cache named 'celery'
|
||||
CELERY_RESULT_BACKEND = 'cache'
|
||||
CELERY_CACHE_BACKEND = 'celery'
|
||||
|
||||
# When the broker is behind an ELB, use a heartbeat to refresh the
|
||||
# connection and to detect if it has been dropped.
|
||||
BROKER_HEARTBEAT = 10.0
|
||||
BROKER_HEARTBEAT_CHECKRATE = 2
|
||||
|
||||
# Each worker should only fetch one message at a time
|
||||
CELERYD_PREFETCH_MULTIPLIER = 1
|
||||
|
||||
# Skip djcelery migrations, since we don't use the database as the broker
|
||||
SOUTH_MIGRATION_MODULES = {
|
||||
'djcelery': 'ignore',
|
||||
}
|
||||
|
||||
# Rename the exchange and queues for each variant
|
||||
|
||||
QUEUE_VARIANT = CONFIG_PREFIX.lower()
|
||||
|
||||
CELERY_DEFAULT_EXCHANGE = 'edx.{0}core'.format(QUEUE_VARIANT)
|
||||
|
||||
HIGH_PRIORITY_QUEUE = 'edx.{0}core.high'.format(QUEUE_VARIANT)
|
||||
DEFAULT_PRIORITY_QUEUE = 'edx.{0}core.default'.format(QUEUE_VARIANT)
|
||||
LOW_PRIORITY_QUEUE = 'edx.{0}core.low'.format(QUEUE_VARIANT)
|
||||
|
||||
CELERY_DEFAULT_QUEUE = DEFAULT_PRIORITY_QUEUE
|
||||
CELERY_DEFAULT_ROUTING_KEY = DEFAULT_PRIORITY_QUEUE
|
||||
|
||||
CELERY_QUEUES = {
|
||||
HIGH_PRIORITY_QUEUE: {},
|
||||
LOW_PRIORITY_QUEUE: {},
|
||||
DEFAULT_PRIORITY_QUEUE: {}
|
||||
}
|
||||
|
||||
############# NON-SECURE ENV CONFIG ##############################
|
||||
# Things like server locations, ports, etc.
|
||||
with open(ENV_ROOT / CONFIG_PREFIX + "env.json") as env_file:
|
||||
@@ -78,3 +120,14 @@ CONTENTSTORE = AUTH_TOKENS['CONTENTSTORE']
|
||||
|
||||
# Datadog for events!
|
||||
DATADOG_API = AUTH_TOKENS.get("DATADOG_API")
|
||||
|
||||
# Celery Broker
|
||||
CELERY_BROKER_TRANSPORT = ENV_TOKENS.get("CELERY_BROKER_TRANSPORT", "")
|
||||
CELERY_BROKER_HOSTNAME = ENV_TOKENS.get("CELERY_BROKER_HOSTNAME", "")
|
||||
CELERY_BROKER_USER = AUTH_TOKENS.get("CELERY_BROKER_USER", "")
|
||||
CELERY_BROKER_PASSWORD = AUTH_TOKENS.get("CELERY_BROKER_PASSWORD", "")
|
||||
|
||||
BROKER_URL = "{0}://{1}:{2}@{3}".format(CELERY_BROKER_TRANSPORT,
|
||||
CELERY_BROKER_USER,
|
||||
CELERY_BROKER_PASSWORD,
|
||||
CELERY_BROKER_HOSTNAME)
|
||||
|
||||
@@ -34,6 +34,9 @@ MITX_FEATURES = {
|
||||
'STAFF_EMAIL': '', # email address for staff (eg to request course creation)
|
||||
'STUDIO_NPS_SURVEY': True,
|
||||
'SEGMENT_IO': True,
|
||||
|
||||
# Enable URL that shows information about the status of variuous services
|
||||
'ENABLE_SERVICE_STATUS': False,
|
||||
}
|
||||
ENABLE_JASMINE = False
|
||||
|
||||
@@ -240,6 +243,51 @@ STATICFILES_IGNORE_PATTERNS = (
|
||||
|
||||
PIPELINE_YUI_BINARY = 'yui-compressor'
|
||||
|
||||
################################# CELERY ######################################
|
||||
|
||||
# Message configuration
|
||||
|
||||
CELERY_TASK_SERIALIZER = 'json'
|
||||
CELERY_RESULT_SERIALIZER = 'json'
|
||||
|
||||
CELERY_MESSAGE_COMPRESSION = 'gzip'
|
||||
|
||||
# Results configuration
|
||||
|
||||
CELERY_IGNORE_RESULT = False
|
||||
CELERY_STORE_ERRORS_EVEN_IF_IGNORED = True
|
||||
|
||||
# Events configuration
|
||||
|
||||
CELERY_TRACK_STARTED = True
|
||||
|
||||
CELERY_SEND_EVENTS = True
|
||||
CELERY_SEND_TASK_SENT_EVENT = True
|
||||
|
||||
# Exchange configuration
|
||||
|
||||
CELERY_DEFAULT_EXCHANGE = 'edx.core'
|
||||
CELERY_DEFAULT_EXCHANGE_TYPE = 'direct'
|
||||
|
||||
# Queues configuration
|
||||
|
||||
HIGH_PRIORITY_QUEUE = 'edx.core.high'
|
||||
DEFAULT_PRIORITY_QUEUE = 'edx.core.default'
|
||||
LOW_PRIORITY_QUEUE = 'edx.core.low'
|
||||
|
||||
CELERY_QUEUE_HA_POLICY = 'all'
|
||||
|
||||
CELERY_CREATE_MISSING_QUEUES = True
|
||||
|
||||
CELERY_DEFAULT_QUEUE = DEFAULT_PRIORITY_QUEUE
|
||||
CELERY_DEFAULT_ROUTING_KEY = DEFAULT_PRIORITY_QUEUE
|
||||
|
||||
CELERY_QUEUES = {
|
||||
HIGH_PRIORITY_QUEUE: {},
|
||||
LOW_PRIORITY_QUEUE: {},
|
||||
DEFAULT_PRIORITY_QUEUE: {}
|
||||
}
|
||||
|
||||
############################ APPS #####################################
|
||||
|
||||
INSTALLED_APPS = (
|
||||
@@ -249,8 +297,12 @@ INSTALLED_APPS = (
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.messages',
|
||||
'djcelery',
|
||||
'south',
|
||||
|
||||
# Monitor the status of services
|
||||
'service_status',
|
||||
|
||||
# For CMS
|
||||
'contentstore',
|
||||
'auth',
|
||||
@@ -265,3 +317,7 @@ INSTALLED_APPS = (
|
||||
'staticfiles',
|
||||
'static_replace',
|
||||
)
|
||||
|
||||
################# EDX MARKETING SITE ##################################
|
||||
|
||||
EDXMKTG_COOKIE_NAME = 'edxloggedin'
|
||||
|
||||
@@ -116,6 +116,11 @@ SECRET_KEY = '85920908f28904ed733fe576320db18cabd7b6cd'
|
||||
|
||||
PIPELINE_SASS_ARGUMENTS = '--debug-info --require {proj_dir}/static/sass/bourbon/lib/bourbon.rb'.format(proj_dir=PROJECT_ROOT)
|
||||
|
||||
################################# CELERY ######################################
|
||||
|
||||
# By default don't use a worker, execute tasks as if they were local functions
|
||||
CELERY_ALWAYS_EAGER = True
|
||||
|
||||
################################ DEBUG TOOLBAR #################################
|
||||
INSTALLED_APPS += ('debug_toolbar', 'debug_toolbar_mongo')
|
||||
MIDDLEWARE_CLASSES += ('django_comment_client.utils.QueryCountDebugMiddleware',
|
||||
@@ -151,5 +156,8 @@ DEBUG_TOOLBAR_MONGO_STACKTRACES = True
|
||||
# disable NPS survey in dev mode
|
||||
MITX_FEATURES['STUDIO_NPS_SURVEY'] = False
|
||||
|
||||
# Enable URL that shows information about the status of variuous services
|
||||
MITX_FEATURES['ENABLE_SERVICE_STATUS'] = True
|
||||
|
||||
# segment-io key for dev
|
||||
SEGMENT_IO_KEY = 'mty8edrrsg'
|
||||
|
||||
35
cms/envs/dev_with_worker.py
Normal file
35
cms/envs/dev_with_worker.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
This config file follows the dev enviroment, but adds the
|
||||
requirement of a celery worker running in the background to process
|
||||
celery tasks.
|
||||
|
||||
The worker can be executed using:
|
||||
|
||||
django_admin.py celery worker
|
||||
"""
|
||||
|
||||
from dev import *
|
||||
|
||||
################################# CELERY ######################################
|
||||
|
||||
# Requires a separate celery worker
|
||||
|
||||
CELERY_ALWAYS_EAGER = False
|
||||
|
||||
# Use django db as the broker and result store
|
||||
|
||||
BROKER_URL = 'django://'
|
||||
INSTALLED_APPS += ('djcelery.transport', )
|
||||
CELERY_RESULT_BACKEND = 'database'
|
||||
DJKOMBU_POLLING_INTERVAL = 1.0
|
||||
|
||||
# Disable transaction management because we are using a worker. Views
|
||||
# that request a task and wait for the result will deadlock otherwise.
|
||||
|
||||
MIDDLEWARE_CLASSES = tuple(
|
||||
c for c in MIDDLEWARE_CLASSES
|
||||
if c != 'django.middleware.transaction.TransactionMiddleware')
|
||||
|
||||
# Note: other alternatives for disabling transactions don't work in 1.4
|
||||
# https://code.djangoproject.com/ticket/2304
|
||||
# https://code.djangoproject.com/ticket/16039
|
||||
@@ -41,14 +41,14 @@ MODULESTORE_OPTIONS = {
|
||||
'default_class': 'xmodule.raw_module.RawDescriptor',
|
||||
'host': 'localhost',
|
||||
'db': 'test_xmodule',
|
||||
'collection': 'modulestore',
|
||||
'fs_root': GITHUB_REPO_ROOT,
|
||||
'collection': 'test_modulestore',
|
||||
'fs_root': TEST_ROOT / "data",
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string',
|
||||
}
|
||||
|
||||
MODULESTORE = {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
|
||||
'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore',
|
||||
'OPTIONS': MODULESTORE_OPTIONS
|
||||
},
|
||||
'direct': {
|
||||
@@ -108,6 +108,12 @@ CACHES = {
|
||||
}
|
||||
}
|
||||
|
||||
################################# CELERY ######################################
|
||||
|
||||
CELERY_ALWAYS_EAGER = True
|
||||
CELERY_RESULT_BACKEND = 'cache'
|
||||
BROKER_TRANSPORT = 'memory'
|
||||
|
||||
################### Make tests faster
|
||||
#http://slacy.com/blog/2012/04/make-your-tests-faster-in-django-1-4/
|
||||
PASSWORD_HASHERS = (
|
||||
@@ -121,3 +127,4 @@ SEGMENT_IO_KEY = '***REMOVED***'
|
||||
# disable NPS survey in test mode
|
||||
MITX_FEATURES['STUDIO_NPS_SURVEY'] = False
|
||||
|
||||
MITX_FEATURES['ENABLE_SERVICE_STATUS'] = True
|
||||
|
||||
@@ -801,7 +801,8 @@ hr.divide {
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
@extend .t-copy-sub2;
|
||||
@include font-size(12);
|
||||
@include transition(opacity 0.1s ease-out);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@@ -811,10 +812,9 @@ hr.divide {
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
font-weight: normal;
|
||||
line-height: 26px;
|
||||
color: #fff;
|
||||
color: $white;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
@include transition(opacity 0.1s ease-out);
|
||||
|
||||
&:after {
|
||||
content: '▾';
|
||||
|
||||
@@ -184,6 +184,6 @@ $lightBluishGrey2: rgb(213, 220, 228);
|
||||
$error-red: rgb(253, 87, 87);
|
||||
|
||||
// type
|
||||
$sans-serif: $f-serif;
|
||||
$sans-serif: $f-sans-serif;
|
||||
$body-line-height: golden-ratio(.875em, 1);
|
||||
|
||||
|
||||
@@ -1,8 +1,49 @@
|
||||
// studio - assets - fonts
|
||||
// NOTE: Sass currently can't process the standard Google Web Font import method, so a @font-face with src declaration of the .woff file that the Google @import method uses is needed :/
|
||||
// ====================
|
||||
|
||||
// import from google fonts - Open Sans
|
||||
@import url(http://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,700italic,400,700,300);
|
||||
// Open Sans - http://www.google.com/fonts/specimen/Open+Sans
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/k3k702ZOKiLJc3WVjuplzKRDOzjiPcYnFooOUGCOsRk.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/DXI1ORHCpsQm3Vp6mXoaTaRDOzjiPcYnFooOUGCOsRk.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/PRmiXeptR36kaC0GEAetxhbnBKKEOwRKgsHDreGcocg.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light Italic'), local('OpenSansLight-Italic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/PRmiXeptR36kaC0GEAetxvR_54zmj3SbGZQh3vCOwvY.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/xjAJXh38I15wypJXxuGMBrrIa-7acMAeDBVuclsi6Gc.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans'), local('OpenSans'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/cJZKeOuBrn4kERxqtaUH3bO3LdcAZYWl9Si6vvxL-qU.woff) format('woff');
|
||||
}
|
||||
|
||||
// import from google fonts - Bree
|
||||
@import url(http://fonts.googleapis.com/css?family=Bree+Serif);
|
||||
// Bree Serif - http://www.google.com/fonts/specimen/Bree+Serif
|
||||
@font-face {
|
||||
font-family: 'Bree Serif';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Bree Serif'), local('BreeSerif'), url(http://themes.googleusercontent.com/static/fonts/breeserif/v2/LQ7WLTaITDg4OSRuOZCps73hpw3pgy2gAi-Ip7WPMi0.woff) format('woff');
|
||||
}
|
||||
|
||||
@@ -106,13 +106,19 @@
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.course-org {
|
||||
margin-right: ($baseline/4);
|
||||
}
|
||||
|
||||
.course-number, .course-org {
|
||||
@include font-size(12);
|
||||
display: inline-block;
|
||||
max-width: 70px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.course-org {
|
||||
margin-right: ($baseline/4);
|
||||
max-width: 140px;
|
||||
}
|
||||
|
||||
.course-title {
|
||||
@@ -132,9 +138,9 @@
|
||||
|
||||
// specific elements - course nav
|
||||
.nav-course {
|
||||
width: 285px;
|
||||
width: 290px;
|
||||
@extend .t-copy-sub1;
|
||||
margin-top: -($baseline/4);
|
||||
@include font-size(14);
|
||||
|
||||
> ol > .nav-item {
|
||||
vertical-align: bottom;
|
||||
@@ -152,8 +158,8 @@
|
||||
color: $gray-d3;
|
||||
|
||||
.label-prefix {
|
||||
display: block;
|
||||
@include font-size(11);
|
||||
display: block;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +135,12 @@ if settings.ENABLE_JASMINE:
|
||||
# # Jasmine
|
||||
urlpatterns = urlpatterns + (url(r'^_jasmine/', include('django_jasmine.urls')),)
|
||||
|
||||
|
||||
if settings.MITX_FEATURES.get('ENABLE_SERVICE_STATUS'):
|
||||
urlpatterns += (
|
||||
url(r'^status/', include('service_status.urls')),
|
||||
)
|
||||
|
||||
urlpatterns = patterns(*urlpatterns)
|
||||
|
||||
# Custom error pages
|
||||
|
||||
Reference in New Issue
Block a user