diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e6d6540c14a8..6dcd6cd48fe2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,9 @@ Blades: Disallow users to enter video url's in http. LMS: Improve the acessibility of the forum follow post buttons. +Blades: Latex problems are now enabled via use_latex_compiler +key in course settings. (BLD-426) + Blades: Fix bug when the speed can only be changed when the video is playing. LMS: Change bulk email implementation to use less memory, and to better handle diff --git a/cms/djangoapps/contentstore/features/advanced-settings.feature b/cms/djangoapps/contentstore/features/advanced_settings.feature similarity index 100% rename from cms/djangoapps/contentstore/features/advanced-settings.feature rename to cms/djangoapps/contentstore/features/advanced_settings.feature diff --git a/cms/djangoapps/contentstore/features/advanced-settings.py b/cms/djangoapps/contentstore/features/advanced_settings.py similarity index 100% rename from cms/djangoapps/contentstore/features/advanced-settings.py rename to cms/djangoapps/contentstore/features/advanced_settings.py diff --git a/cms/djangoapps/contentstore/features/component.feature b/cms/djangoapps/contentstore/features/component.feature index 95de94c4bd6f..e2cc4bc3321d 100644 --- a/cms/djangoapps/contentstore/features/component.feature +++ b/cms/djangoapps/contentstore/features/component.feature @@ -19,11 +19,19 @@ Feature: CMS.Component Adding | Component | | Text | | Announcement | - | E-text Written in LaTeX | Then I see HTML components in this order: | Component | | Text | | Announcement | + + Scenario: I can add Latex HTML components + Given I am in Studio editing a new unit + Given I have enabled latex compiler + When I add this type of HTML component: + | Component | + | E-text Written in LaTeX | + Then I see HTML components in this order: + | Component | | E-text Written in LaTeX | Scenario: I can add Common Problem components @@ -58,9 +66,22 @@ Feature: CMS.Component Adding | Drag and Drop | | Image Mapped Input | | Math Expression Input | - | Problem Written in LaTeX | | Problem with Adaptive Hint | + + Scenario: I can add Advanced Latex Problem components + Given I am in Studio editing a new unit + Given I have enabled latex compiler + When I add a "" "Advanced Problem" component + Then I see a "" Problem component + # Flush out the database before the next example executes + And I reset the database + + Examples: + | Component | + | Problem Written in LaTeX | + | Problem with Adaptive Hint in Latex | + Scenario: I see a prompt on delete Given I am in Studio editing a new unit And I add a "Discussion" "single step" component diff --git a/cms/djangoapps/contentstore/features/html-editor.py b/cms/djangoapps/contentstore/features/html-editor.py index f00db67c071d..4a6d3bdefeb5 100644 --- a/cms/djangoapps/contentstore/features/html-editor.py +++ b/cms/djangoapps/contentstore/features/html-editor.py @@ -22,6 +22,7 @@ def i_see_only_the_html_display_name(step): @step('I have created an E-text Written in LaTeX$') def i_created_etext_in_latex(step): world.create_course_with_unit() + step.given('I have enabled latex compiler') world.create_component_instance( step=step, category='html', diff --git a/cms/djangoapps/contentstore/features/problem-editor.py b/cms/djangoapps/contentstore/features/problem-editor.py index 380845897308..758ac299bed4 100644 --- a/cms/djangoapps/contentstore/features/problem-editor.py +++ b/cms/djangoapps/contentstore/features/problem-editor.py @@ -5,6 +5,7 @@ from lettuce import world, step from nose.tools import assert_equal, assert_true # pylint: disable=E0611 from common import type_in_codemirror, open_new_course +from advanced_settings import change_value from course_import import import_file, go_to_import from selenium.webdriver.common.keys import Keys @@ -159,9 +160,19 @@ def cancel_does_not_save_changes(step): step.given("I see the advanced settings and their expected values") +@step('I have enabled latex compiler') +def enable_latex_compiler(step): + url = world.browser.url + step.given("I select the Advanced Settings") + change_value(step, 'use_latex_compiler', True) + world.visit(url) + world.wait_for_xmodule() + + @step('I have created a LaTeX Problem') def create_latex_problem(step): world.create_course_with_unit() + step.given('I have enabled latex compiler') world.create_component_instance( step=step, category='problem', diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index 44d892e1f9d7..f51789739c76 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -193,12 +193,14 @@ def edit_unit(request, location): # add boilerplates if hasattr(component_class, 'templates'): for template in component_class.templates(): - component_templates[category].append(( - template['metadata'].get('display_name'), - category, - template['metadata'].get('markdown') is not None, - template.get('template_id') - )) + filter_templates = getattr(component_class, 'filter_templates', None) + if not filter_templates or filter_templates(template, course): + component_templates[category].append(( + template['metadata'].get('display_name'), + category, + template['metadata'].get('markdown') is not None, + template.get('template_id') + )) # Check if there are any advanced modules specified in the course policy. # These modules should be specified as a list of strings, where the strings diff --git a/cms/templates/widgets/metadata-edit.html b/cms/templates/widgets/metadata-edit.html index 70a2df3400fb..4ce5f3bfeb4b 100644 --- a/cms/templates/widgets/metadata-edit.html +++ b/cms/templates/widgets/metadata-edit.html @@ -29,7 +29,7 @@ <%static:include path="js/metadata-list-entry.underscore" /> -<% showHighLevelSource='source_code' in editable_metadata_fields and editable_metadata_fields['source_code']['explicitly_set'] %> +<% showHighLevelSource='source_code' in editable_metadata_fields and editable_metadata_fields['source_code']['explicitly_set'] and enable_latex_compiler %> <% metadata_field_copy = copy.copy(editable_metadata_fields) %> ## Delete 'source_code' field (if it exists) so metadata editor view does not attempt to render it. % if 'source_code' in editable_metadata_fields: diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 586464b0589d..27184bb46b73 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -158,6 +158,11 @@ class CapaFields(object): # TODO: someday it should be possible to not duplicate this definition here # and in inheritance.py ) + use_latex_compiler = Boolean( + help="Enable LaTeX templates?", + default=False, + scope=Scope.settings + ) class CapaModule(CapaFields, XModule): @@ -1174,10 +1179,23 @@ class CapaDescriptor(CapaFields, RawDescriptor): metadata_translations = dict(RawDescriptor.metadata_translations) metadata_translations['attempts'] = 'max_attempts' + @classmethod + def filter_templates(cls, template, course): + """ + Filter template that contains 'latex' from templates. + + Show them only if use_latex_compiler is set to True in + course settings. + """ + return (not 'latex' in template['template_id'] or course.use_latex_compiler) + def get_context(self): _context = RawDescriptor.get_context(self) - _context.update({'markdown': self.markdown, - 'enable_markdown': self.markdown is not None}) + _context.update({ + 'markdown': self.markdown, + 'enable_markdown': self.markdown is not None, + 'enable_latex_compiler': self.use_latex_compiler, + }) return _context # VS[compat] @@ -1193,9 +1211,14 @@ def backcompat_paths(cls, path): @property def non_editable_metadata_fields(self): non_editable_fields = super(CapaDescriptor, self).non_editable_metadata_fields - non_editable_fields.extend([CapaDescriptor.due, CapaDescriptor.graceperiod, - CapaDescriptor.force_save_button, CapaDescriptor.markdown, - CapaDescriptor.text_customization]) + non_editable_fields.extend([ + CapaDescriptor.due, + CapaDescriptor.graceperiod, + CapaDescriptor.force_save_button, + CapaDescriptor.markdown, + CapaDescriptor.text_customization, + CapaDescriptor.use_latex_compiler, + ]) return non_editable_fields # Proxy to CapaModule for access to any of its attributes diff --git a/common/lib/xmodule/xmodule/html_module.py b/common/lib/xmodule/xmodule/html_module.py index 5b3ca5661ab6..25cebe8e420f 100644 --- a/common/lib/xmodule/xmodule/html_module.py +++ b/common/lib/xmodule/xmodule/html_module.py @@ -7,7 +7,7 @@ from path import path from pkg_resources import resource_string -from xblock.fields import Scope, String +from xblock.fields import Scope, String, Boolean from xmodule.editing_module import EditingDescriptor from xmodule.html_checker import check_html from xmodule.stringify import stringify_children @@ -30,6 +30,11 @@ class HtmlFields(object): ) data = String(help="Html contents to display for this module", default=u"", scope=Scope.content) source_code = String(help="Source code for LaTeX documents. This feature is not well-supported.", scope=Scope.settings) + use_latex_compiler = Boolean( + help="Enable LaTeX templates?", + default=False, + scope=Scope.settings + ) class HtmlModule(HtmlFields, XModule): @@ -82,6 +87,16 @@ def backcompat_paths(cls, path): nc.append(candidate[:-4] + '.html') return candidates + nc + @classmethod + def filter_templates(cls, template, course): + """ + Filter template that contains 'latex' from templates. + + Show them only if use_latex_compiler is set to True in + course settings. + """ + return (not 'latex' in template['template_id'] or course.use_latex_compiler) + def get_context(self): """ an override to add in specific rendering context, in this case we need to @@ -90,7 +105,10 @@ def get_context(self): _context = EditingDescriptor.get_context(self) # Add some specific HTML rendering context when editing HTML modules where we pass # the root /c4x/ url for assets. This allows client-side substitutions to occur. - _context.update({'base_asset_url': StaticContent.get_base_url_path_for_course_assets(self.location) + '/'}) + _context.update({ + 'base_asset_url': StaticContent.get_base_url_path_for_course_assets(self.location) + '/', + 'enable_latex_compiler': self.use_latex_compiler, + }) return _context # NOTE: html descriptors are special. We do not want to parse and @@ -191,6 +209,12 @@ def definition_to_xml(self, resource_fs): elt.set("filename", relname) return elt + @property + def non_editable_metadata_fields(self): + non_editable_fields = super(HtmlDescriptor, self).non_editable_metadata_fields + non_editable_fields.append(HtmlDescriptor.use_latex_compiler) + return non_editable_fields + class AboutFields(object): display_name = String( diff --git a/common/lib/xmodule/xmodule/modulestore/inheritance.py b/common/lib/xmodule/xmodule/modulestore/inheritance.py index a518034e610f..8962447ec0ba 100644 --- a/common/lib/xmodule/xmodule/modulestore/inheritance.py +++ b/common/lib/xmodule/xmodule/modulestore/inheritance.py @@ -47,6 +47,10 @@ class InheritanceMixin(XBlockMixin): help="String customization substitutions for particular locations", scope=Scope.settings ) + use_latex_compiler = Boolean( + help="Enable LaTeX templates?", + default=False, + scope=Scope.settings) def compute_inherited_metadata(descriptor): diff --git a/common/lib/xmodule/xmodule/templates/problem/problem_with_hint.yaml b/common/lib/xmodule/xmodule/templates/problem/problem_with_hint.yaml index aa1000a93a4f..f9a171b3e1af 100644 --- a/common/lib/xmodule/xmodule/templates/problem/problem_with_hint.yaml +++ b/common/lib/xmodule/xmodule/templates/problem/problem_with_hint.yaml @@ -1,51 +1,6 @@ --- metadata: display_name: Problem with Adaptive Hint - source_code: | - \subsection{Problem With Adaptive Hint} - - % Adaptive hints are messages provided to students which depend on - % student input. These hints are produced using a script embedded - % within the problem (written in Python). - % - % Here is an example. This example uses LaTeX as a high-level - % soure language for the problem. The problem can also be coded - % directly in XML. - - This problem demonstrates a question with hints, based on using the - {\tt hintfn} method. - - \begin{edXscript} - def test_str(expect, ans): - print expect, ans - ans = ans.strip("'") - ans = ans.strip('"') - return expect == ans.lower() - - def hint_fn(answer_ids, student_answers, new_cmap, old_cmap): - aid = answer_ids[0] - ans = str(student_answers[aid]).lower() - print 'hint_fn called, ans=', ans - hint = '' - if 'java' in ans: - hint = 'that is only good for drinking' - elif 'perl' in ans: - hint = 'not that rich' - elif 'pascal' in ans: - hint = 'that is a beatnick language' - elif 'fortran' in ans: - hint = 'those were the good days' - elif 'clu' in ans: - hint = 'you must be invariant' - if hint: - hint = "Hint: {0}".format(hint) - new_cmap.set_hint_and_mode(aid,hint,'always') - \end{edXscript} - - What is the best programming language that exists today? You may - enter your answer in upper or lower case, with or without quotes. - - \edXabox{type="custom" cfn='test_str' expect='python' hintfn='hint_fn'} markdown: !!null data: | @@ -61,7 +16,7 @@ data: | ans = ans.strip("'") ans = ans.strip('"') return expect == ans.lower() - + def hint_fn(answer_ids, student_answers, new_cmap, old_cmap): aid = answer_ids[0] ans = str(student_answers[aid]).lower() diff --git a/common/lib/xmodule/xmodule/templates/problem/problem_with_hint_in_latex.yaml b/common/lib/xmodule/xmodule/templates/problem/problem_with_hint_in_latex.yaml new file mode 100644 index 000000000000..223d16ae9d38 --- /dev/null +++ b/common/lib/xmodule/xmodule/templates/problem/problem_with_hint_in_latex.yaml @@ -0,0 +1,93 @@ +--- +metadata: + display_name: Problem with Adaptive Hint in Latex + source_code: | + \subsection{Problem With Adaptive Hint} + + % Adaptive hints are messages provided to students which depend on + % student input. These hints are produced using a script embedded + % within the problem (written in Python). + % + % Here is an example. This example uses LaTeX as a high-level + % soure language for the problem. The problem can also be coded + % directly in XML. + + This problem demonstrates a question with hints, based on using the + {\tt hintfn} method. + + \begin{edXscript} + def test_str(expect, ans): + print expect, ans + ans = ans.strip("'") + ans = ans.strip('"') + return expect == ans.lower() + + def hint_fn(answer_ids, student_answers, new_cmap, old_cmap): + aid = answer_ids[0] + ans = str(student_answers[aid]).lower() + print 'hint_fn called, ans=', ans + hint = '' + if 'java' in ans: + hint = 'that is only good for drinking' + elif 'perl' in ans: + hint = 'not that rich' + elif 'pascal' in ans: + hint = 'that is a beatnick language' + elif 'fortran' in ans: + hint = 'those were the good days' + elif 'clu' in ans: + hint = 'you must be invariant' + if hint: + hint = "Hint: {0}".format(hint) + new_cmap.set_hint_and_mode(aid,hint,'always') + \end{edXscript} + + What is the best programming language that exists today? You may + enter your answer in upper or lower case, with or without quotes. + + \edXabox{type="custom" cfn='test_str' expect='python' hintfn='hint_fn'} + markdown: !!null +data: | + + +

+

Problem With Adaptive Hint

+

+

+ This problem demonstrates a question with hints, based on using the hintfn method.

+ +

+ What is the best programming language that exists today? You may enter your answer in upper or lower case, with or without quotes.

+

+ + + + +

+
+