Merge pull request #6459 from edx/content-libraries
Content libraries MVP
This commit is contained in:
@@ -32,6 +32,10 @@ class StudentModule(models.Model):
|
||||
MODULE_TYPES = (('problem', 'problem'),
|
||||
('video', 'video'),
|
||||
('html', 'html'),
|
||||
('course', 'course'),
|
||||
('chapter', 'Section'),
|
||||
('sequential', 'Subsection'),
|
||||
('library_content', 'Library Content'),
|
||||
)
|
||||
## These three are the key for the object
|
||||
module_type = models.CharField(max_length=32, choices=MODULE_TYPES, default='problem', db_index=True)
|
||||
|
||||
@@ -755,6 +755,7 @@ def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, user):
|
||||
|
||||
try:
|
||||
descriptor = modulestore().get_item(usage_key)
|
||||
descriptor_orig_usage_key, descriptor_orig_version = modulestore().get_block_original_usage(usage_key)
|
||||
except ItemNotFoundError:
|
||||
log.warn(
|
||||
"Invalid location for course id {course_id}: {usage_key}".format(
|
||||
@@ -768,8 +769,13 @@ def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, user):
|
||||
tracking_context = {
|
||||
'module': {
|
||||
'display_name': descriptor.display_name_with_default,
|
||||
'usage_key': unicode(descriptor.location),
|
||||
}
|
||||
}
|
||||
# For blocks that are inherited from a content library, we add some additional metadata:
|
||||
if descriptor_orig_usage_key is not None:
|
||||
tracking_context['module']['original_usage_key'] = unicode(descriptor_orig_usage_key)
|
||||
tracking_context['module']['original_usage_version'] = unicode(descriptor_orig_version)
|
||||
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
course_id,
|
||||
|
||||
@@ -5,6 +5,7 @@ Test for lms courseware app, module render unit
|
||||
from functools import partial
|
||||
import json
|
||||
|
||||
from bson import ObjectId
|
||||
import ddt
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.core.urlresolvers import reverse
|
||||
@@ -13,6 +14,7 @@ from django.test.client import RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from mock import MagicMock, patch, Mock
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from xblock.field_data import FieldData
|
||||
from xblock.runtime import Runtime
|
||||
@@ -971,12 +973,13 @@ class TestModuleTrackingContext(ModuleStoreTestCase):
|
||||
|
||||
def test_context_contains_display_name(self, mock_tracker):
|
||||
problem_display_name = u'Option Response Problem'
|
||||
actual_display_name = self.handle_callback_and_get_display_name_from_event(mock_tracker, problem_display_name)
|
||||
self.assertEquals(problem_display_name, actual_display_name)
|
||||
module_info = self.handle_callback_and_get_module_info(mock_tracker, problem_display_name)
|
||||
self.assertEquals(problem_display_name, module_info['display_name'])
|
||||
|
||||
def handle_callback_and_get_display_name_from_event(self, mock_tracker, problem_display_name=None):
|
||||
def handle_callback_and_get_module_info(self, mock_tracker, problem_display_name=None):
|
||||
"""
|
||||
Creates a fake module, invokes the callback and extracts the display name from the emitted problem_check event.
|
||||
Creates a fake module, invokes the callback and extracts the 'module'
|
||||
metadata from the emitted problem_check event.
|
||||
"""
|
||||
descriptor_kwargs = {
|
||||
'category': 'problem',
|
||||
@@ -1000,12 +1003,28 @@ class TestModuleTrackingContext(ModuleStoreTestCase):
|
||||
event = mock_call[1][0]
|
||||
|
||||
self.assertEquals(event['event_type'], 'problem_check')
|
||||
return event['context']['module']['display_name']
|
||||
return event['context']['module']
|
||||
|
||||
def test_missing_display_name(self, mock_tracker):
|
||||
actual_display_name = self.handle_callback_and_get_display_name_from_event(mock_tracker)
|
||||
actual_display_name = self.handle_callback_and_get_module_info(mock_tracker)['display_name']
|
||||
self.assertTrue(actual_display_name.startswith('problem'))
|
||||
|
||||
def test_library_source_information(self, mock_tracker):
|
||||
"""
|
||||
Check that XBlocks that are inherited from a library include the
|
||||
information about their library block source in events.
|
||||
We patch the modulestore to avoid having to create a library.
|
||||
"""
|
||||
original_usage_key = UsageKey.from_string(u'block-v1:A+B+C+type@problem+block@abcd1234')
|
||||
original_usage_version = ObjectId()
|
||||
mock_get_original_usage = lambda _, key: (original_usage_key, original_usage_version)
|
||||
with patch('xmodule.modulestore.mixed.MixedModuleStore.get_block_original_usage', mock_get_original_usage):
|
||||
module_info = self.handle_callback_and_get_module_info(mock_tracker)
|
||||
self.assertIn('original_usage_key', module_info)
|
||||
self.assertEqual(module_info['original_usage_key'], unicode(original_usage_key))
|
||||
self.assertIn('original_usage_version', module_info)
|
||||
self.assertEqual(module_info['original_usage_version'], unicode(original_usage_version))
|
||||
|
||||
|
||||
class TestXmoduleRuntimeEvent(TestSubmittingProblems):
|
||||
"""
|
||||
|
||||
@@ -10,6 +10,7 @@ from django.conf import settings
|
||||
from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig
|
||||
from openedx.core.djangoapps.user_api.api import course_tag as user_course_tag_api
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.library_tools import LibraryToolsService
|
||||
from xmodule.x_module import ModuleSystem
|
||||
from xmodule.partitions.partitions_service import PartitionService
|
||||
|
||||
@@ -199,6 +200,7 @@ class LmsModuleSystem(LmsHandlerUrls, ModuleSystem): # pylint: disable=abstract
|
||||
course_id=kwargs.get('course_id'),
|
||||
track_function=kwargs.get('track_function', None),
|
||||
)
|
||||
services['library_tools'] = LibraryToolsService(modulestore())
|
||||
services['fs'] = xblock.reference.plugins.FSService()
|
||||
self.request_token = kwargs.pop('request_token', None)
|
||||
super(LmsModuleSystem, self).__init__(**kwargs)
|
||||
|
||||
14
lms/templates/library-block-author-preview-header.html
Normal file
14
lms/templates/library-block-author-preview-header.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<%! from django.utils.translation import ungettext %>
|
||||
<div class="wrapper-xblock-message">
|
||||
<div class="xblock-message information">
|
||||
<p>
|
||||
<span class="message-text">
|
||||
${ungettext(
|
||||
'Showing all matching content eligible to be added into {display_name}. Each student will be assigned {max_count} component drawn randomly from this list.',
|
||||
'Showing all matching content eligible to be added into {display_name}. Each student will be assigned {max_count} components drawn randomly from this list.',
|
||||
max_count
|
||||
).format(max_count=max_count, display_name=display_name)}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## The JS for this is defined in xqa_interface.html
|
||||
${block_content}
|
||||
%if location.category in ['problem','video','html','combinedopenended','graphical_slider_tool']:
|
||||
%if location.category in ['problem','video','html','combinedopenended','graphical_slider_tool', 'library_content']:
|
||||
% if edit_link:
|
||||
<div>
|
||||
<a href="${edit_link}">Edit</a>
|
||||
|
||||
25
lms/templates/studio_render_paged_children_view.html
Normal file
25
lms/templates/studio_render_paged_children_view.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
|
||||
% for template_name in ["paging-header", "paging-footer"]:
|
||||
<script type="text/template" id="${template_name}-tpl">
|
||||
<%static:include path="js/${template_name}.underscore" />
|
||||
</script>
|
||||
% endfor
|
||||
|
||||
<div class="xblock-container-paging-parameters" data-start="${first_displayed}" data-displayed="${displayed_children}" data-total="${total_children}"></div>
|
||||
|
||||
<div class="container-paging-header"></div>
|
||||
|
||||
<div>
|
||||
% for item in items:
|
||||
${item['content']}
|
||||
% endfor
|
||||
</div>
|
||||
|
||||
% if can_add:
|
||||
<div class="add-xblock-component new-component-item adding"></div>
|
||||
% endif
|
||||
|
||||
<div class="container-paging-footer"></div>
|
||||
Reference in New Issue
Block a user