Merge pull request #6321 from edx/feature/edxnotes
TNL-213: Student Notes
This commit is contained in:
15
common/lib/xmodule/xmodule/edxnotes_utils.py
Normal file
15
common/lib/xmodule/xmodule/edxnotes_utils.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""
|
||||
Utilities related to edXNotes.
|
||||
"""
|
||||
import sys
|
||||
|
||||
|
||||
def edxnotes(cls):
|
||||
"""
|
||||
Conditional decorator that loads edxnotes only when they exist.
|
||||
"""
|
||||
if "edxnotes" in sys.modules:
|
||||
from edxnotes.decorators import edxnotes as notes # pylint: disable=import-error
|
||||
return notes(cls)
|
||||
else:
|
||||
return cls
|
||||
@@ -16,6 +16,8 @@ from xmodule.xml_module import XmlDescriptor, name_to_pathname
|
||||
import textwrap
|
||||
from xmodule.contentstore.content import StaticContent
|
||||
from xblock.core import XBlock
|
||||
from xmodule.edxnotes_utils import edxnotes
|
||||
|
||||
|
||||
log = logging.getLogger("edx.courseware")
|
||||
|
||||
@@ -51,7 +53,10 @@ class HtmlFields(object):
|
||||
)
|
||||
|
||||
|
||||
class HtmlModule(HtmlFields, XModule):
|
||||
class HtmlModuleMixin(HtmlFields, XModule):
|
||||
"""
|
||||
Attributes and methods used by HtmlModules internally.
|
||||
"""
|
||||
js = {
|
||||
'coffee': [
|
||||
resource_string(__name__, 'js/src/javascript_loader.coffee'),
|
||||
@@ -72,6 +77,14 @@ class HtmlModule(HtmlFields, XModule):
|
||||
return self.data
|
||||
|
||||
|
||||
@edxnotes
|
||||
class HtmlModule(HtmlModuleMixin):
|
||||
"""
|
||||
Module for putting raw html in a course
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor):
|
||||
"""
|
||||
Module for putting raw html in a course
|
||||
@@ -255,7 +268,7 @@ class AboutFields(object):
|
||||
|
||||
|
||||
@XBlock.tag("detached")
|
||||
class AboutModule(AboutFields, HtmlModule):
|
||||
class AboutModule(AboutFields, HtmlModuleMixin):
|
||||
"""
|
||||
Overriding defaults but otherwise treated as HtmlModule.
|
||||
"""
|
||||
@@ -292,7 +305,7 @@ class StaticTabFields(object):
|
||||
|
||||
|
||||
@XBlock.tag("detached")
|
||||
class StaticTabModule(StaticTabFields, HtmlModule):
|
||||
class StaticTabModule(StaticTabFields, HtmlModuleMixin):
|
||||
"""
|
||||
Supports the field overrides
|
||||
"""
|
||||
@@ -326,7 +339,7 @@ class CourseInfoFields(object):
|
||||
|
||||
|
||||
@XBlock.tag("detached")
|
||||
class CourseInfoModule(CourseInfoFields, HtmlModule):
|
||||
class CourseInfoModule(CourseInfoFields, HtmlModuleMixin):
|
||||
"""
|
||||
Just to support xblock field overrides
|
||||
"""
|
||||
|
||||
@@ -35,7 +35,7 @@ src_paths:
|
||||
lib_paths:
|
||||
- common_static/js/test/i18n.js
|
||||
- common_static/coffee/src/ajax_prefix.js
|
||||
- common_static/coffee/src/logger.js
|
||||
- common_static/js/src/logger.js
|
||||
- common_static/js/vendor/jasmine-jquery.js
|
||||
- common_static/js/vendor/jasmine-imagediff.js
|
||||
- common_static/js/vendor/require.js
|
||||
|
||||
@@ -34,7 +34,7 @@ describe 'Crowdsourced hinter', ->
|
||||
response =
|
||||
success: 'incorrect'
|
||||
contents: 'mock grader response'
|
||||
settings.success(response)
|
||||
settings.success(response) if settings
|
||||
)
|
||||
@problem.answers = 'test answer'
|
||||
@problem.check_fd()
|
||||
|
||||
@@ -172,6 +172,19 @@ class InheritanceMixin(XBlockMixin):
|
||||
scope=Scope.settings,
|
||||
default=default_reset_button
|
||||
)
|
||||
edxnotes = Boolean(
|
||||
display_name=_("Enable Student Notes"),
|
||||
help=_("Enter true or false. If true, students can use the Student Notes feature."),
|
||||
default=False,
|
||||
scope=Scope.settings
|
||||
)
|
||||
edxnotes_visibility = Boolean(
|
||||
display_name="Student Notes Visibility",
|
||||
help=_("Indicates whether Student Notes are visible in the course. "
|
||||
"Students can also show or hide their notes in the courseware."),
|
||||
default=True,
|
||||
scope=Scope.user_info
|
||||
)
|
||||
|
||||
|
||||
def compute_inherited_metadata(descriptor):
|
||||
|
||||
@@ -69,6 +69,7 @@ class CourseTab(object): # pylint: disable=incomplete-protocol
|
||||
settings: The configuration settings, including values for:
|
||||
WIKI_ENABLED
|
||||
FEATURES['ENABLE_DISCUSSION_SERVICE']
|
||||
FEATURES['ENABLE_EDXNOTES']
|
||||
FEATURES['ENABLE_STUDENT_NOTES']
|
||||
FEATURES['ENABLE_TEXTBOOK']
|
||||
|
||||
@@ -195,6 +196,7 @@ class CourseTab(object): # pylint: disable=incomplete-protocol
|
||||
'staff_grading': StaffGradingTab,
|
||||
'open_ended': OpenEndedGradingTab,
|
||||
'notes': NotesTab,
|
||||
'edxnotes': EdxNotesTab,
|
||||
'syllabus': SyllabusTab,
|
||||
'instructor': InstructorTab, # not persisted
|
||||
}
|
||||
@@ -694,6 +696,27 @@ class NotesTab(AuthenticatedCourseTab):
|
||||
return super(NotesTab, cls).validate(tab_dict, raise_error) and need_name(tab_dict, raise_error)
|
||||
|
||||
|
||||
class EdxNotesTab(AuthenticatedCourseTab):
|
||||
"""
|
||||
A tab for the course student notes.
|
||||
"""
|
||||
type = 'edxnotes'
|
||||
|
||||
def can_display(self, course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
|
||||
return settings.FEATURES.get('ENABLE_EDXNOTES')
|
||||
|
||||
def __init__(self, tab_dict=None):
|
||||
super(EdxNotesTab, self).__init__(
|
||||
name=tab_dict['name'] if tab_dict else _('Notes'),
|
||||
tab_id=self.type,
|
||||
link_func=link_reverse_func(self.type),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def validate(cls, tab_dict, raise_error=True):
|
||||
return super(EdxNotesTab, cls).validate(tab_dict, raise_error) and need_name(tab_dict, raise_error)
|
||||
|
||||
|
||||
class InstructorTab(StaffTab):
|
||||
"""
|
||||
A tab for the course instructors.
|
||||
@@ -854,13 +877,13 @@ class CourseTabList(List):
|
||||
|
||||
# the following tabs should appear only once
|
||||
for tab_type in [
|
||||
CoursewareTab.type,
|
||||
CourseInfoTab.type,
|
||||
NotesTab.type,
|
||||
TextbookTabs.type,
|
||||
PDFTextbookTabs.type,
|
||||
HtmlTextbookTabs.type,
|
||||
]:
|
||||
CoursewareTab.type,
|
||||
CourseInfoTab.type,
|
||||
NotesTab.type,
|
||||
TextbookTabs.type,
|
||||
PDFTextbookTabs.type,
|
||||
HtmlTextbookTabs.type,
|
||||
EdxNotesTab.type]:
|
||||
cls._validate_num_tabs_of_type(tabs, tab_type, 1)
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -412,6 +412,40 @@ class InstructorTestCase(TabTestCase):
|
||||
self.check_can_display_results(tab, for_staff_only=True)
|
||||
|
||||
|
||||
class EdxNotesTestCase(TabTestCase):
|
||||
"""
|
||||
Test cases for Notes Tab.
|
||||
"""
|
||||
|
||||
def check_edxnotes_tab(self):
|
||||
"""
|
||||
Helper function for verifying the edxnotes tab.
|
||||
"""
|
||||
return self.check_tab(
|
||||
tab_class=tabs.EdxNotesTab,
|
||||
dict_tab={'type': tabs.EdxNotesTab.type, 'name': 'same'},
|
||||
expected_link=self.reverse('edxnotes', args=[self.course.id.to_deprecated_string()]),
|
||||
expected_tab_id=tabs.EdxNotesTab.type,
|
||||
invalid_dict_tab=self.fake_dict_tab,
|
||||
)
|
||||
|
||||
def test_edxnotes_tabs_enabled(self):
|
||||
"""
|
||||
Tests that edxnotes tab is shown when feature is enabled.
|
||||
"""
|
||||
self.settings.FEATURES['ENABLE_EDXNOTES'] = True
|
||||
tab = self.check_edxnotes_tab()
|
||||
self.check_can_display_results(tab, for_authenticated_users_only=True)
|
||||
|
||||
def test_edxnotes_tabs_disabled(self):
|
||||
"""
|
||||
Tests that edxnotes tab is not shown when feature is disabled.
|
||||
"""
|
||||
self.settings.FEATURES['ENABLE_EDXNOTES'] = False
|
||||
tab = self.check_edxnotes_tab()
|
||||
self.check_can_display_results(tab, expected_value=False)
|
||||
|
||||
|
||||
class KeyCheckerTestCase(unittest.TestCase):
|
||||
"""Test cases for KeyChecker class"""
|
||||
|
||||
@@ -473,6 +507,7 @@ class TabListTestCase(TabTestCase):
|
||||
tabs.TextbookTabs.type,
|
||||
tabs.PDFTextbookTabs.type,
|
||||
tabs.HtmlTextbookTabs.type,
|
||||
tabs.EdxNotesTab.type,
|
||||
]
|
||||
|
||||
for unique_tab_type in unique_tab_types:
|
||||
@@ -505,6 +540,7 @@ class TabListTestCase(TabTestCase):
|
||||
{'type': tabs.OpenEndedGradingTab.type},
|
||||
{'type': tabs.NotesTab.type, 'name': 'fake_name'},
|
||||
{'type': tabs.SyllabusTab.type},
|
||||
{'type': tabs.EdxNotesTab.type, 'name': 'fake_name'},
|
||||
],
|
||||
# with external discussion
|
||||
[
|
||||
@@ -565,6 +601,7 @@ class CourseTabListTestCase(TabListTestCase):
|
||||
self.settings.FEATURES['ENABLE_TEXTBOOK'] = True
|
||||
self.settings.FEATURES['ENABLE_DISCUSSION_SERVICE'] = True
|
||||
self.settings.FEATURES['ENABLE_STUDENT_NOTES'] = True
|
||||
self.settings.FEATURES['ENABLE_EDXNOTES'] = True
|
||||
self.course.hide_progress_tab = False
|
||||
|
||||
# create 1 book per textbook type
|
||||
|
||||
Reference in New Issue
Block a user