diff --git a/cms/djangoapps/contentstore/features/component_settings_editor_helpers.py b/cms/djangoapps/contentstore/features/component_settings_editor_helpers.py index 6e35d1711f..49f53cfc49 100644 --- a/cms/djangoapps/contentstore/features/component_settings_editor_helpers.py +++ b/cms/djangoapps/contentstore/features/component_settings_editor_helpers.py @@ -7,11 +7,13 @@ from terrain.steps import reload_the_page import time + @world.absorb def create_component_instance(step, component_button_css, instance_id, expected_css): click_new_component_button(step, component_button_css) click_component_from_menu(instance_id, expected_css) + @world.absorb def click_new_component_button(step, component_button_css): step.given('I have opened a new course section in Studio') @@ -20,6 +22,7 @@ def click_new_component_button(step, component_button_css): world.css_click('a.new-unit-item') world.css_click(component_button_css) + @world.absorb def click_component_from_menu(instance_id, expected_css): new_instance = world.browser.find_by_id(instance_id) @@ -29,11 +32,13 @@ def click_component_from_menu(instance_id, expected_css): new_instance[0].click() assert_equal(1, len(world.css_find(expected_css))) + @world.absorb def edit_component_and_select_settings(): world.css_click('a.edit-button') world.css_click('#settings-mode') + @world.absorb def verify_setting_entry(setting, display_name, value, explicitly_set): assert_equal(display_name, setting.find_by_css('.setting-label')[0].value) @@ -42,13 +47,17 @@ def verify_setting_entry(setting, display_name, value, explicitly_set): assert_equal(explicitly_set, settingClearButton.has_class('active')) assert_equal(not explicitly_set, settingClearButton.has_class('inactive')) + @world.absorb def verify_all_setting_entries(expected_entries): settings = world.browser.find_by_css('.wrapper-comp-setting') assert_equal(len(expected_entries), len(settings)) for (counter, setting) in enumerate(settings): - world.verify_setting_entry(setting, expected_entries[counter][0], - expected_entries[counter][1], expected_entries[counter][2]) + world.verify_setting_entry( + setting, expected_entries[counter][0], + expected_entries[counter][1], expected_entries[counter][2] + ) + @world.absorb def save_component_and_reopen(step): @@ -58,6 +67,7 @@ def save_component_and_reopen(step): reload_the_page(step) edit_component_and_select_settings() + @world.absorb def cancel_component(step): world.css_click("a.cancel-button") @@ -65,10 +75,12 @@ def cancel_component(step): # they are not persisted. Refresh the browser to make sure the changes were not persisted. reload_the_page(step) + @world.absorb def revert_setting_entry(label): get_setting_entry(label).find_by_css('.setting-clear')[0].click() + @world.absorb def get_setting_entry(label): settings = world.browser.find_by_css('.wrapper-comp-setting') @@ -76,5 +88,3 @@ def get_setting_entry(label): if setting.find_by_css('.setting-label')[0].value == label: return setting return None - - diff --git a/cms/djangoapps/contentstore/features/discussion-editor.py b/cms/djangoapps/contentstore/features/discussion-editor.py index 4e661a89e1..fa4fb661c6 100644 --- a/cms/djangoapps/contentstore/features/discussion-editor.py +++ b/cms/djangoapps/contentstore/features/discussion-editor.py @@ -3,10 +3,14 @@ from lettuce import world, step + @step('I have created a Discussion Tag$') def i_created_blank_common_problem(step): - world.create_component_instance(step, '.large-discussion-icon', 'i4x://edx/templates/discussion/Discussion_Tag', - '.xmodule_DiscussionModule') + world.create_component_instance( + step, '.large-discussion-icon', 'i4x://edx/templates/discussion/Discussion_Tag', + '.xmodule_DiscussionModule' + ) + @step('I see three alphabetized settings and their expected values$') def i_see_only_the_display_name(step): diff --git a/cms/djangoapps/contentstore/features/html-editor.py b/cms/djangoapps/contentstore/features/html-editor.py index 564d4d986c..02198a5da7 100644 --- a/cms/djangoapps/contentstore/features/html-editor.py +++ b/cms/djangoapps/contentstore/features/html-editor.py @@ -3,10 +3,14 @@ from lettuce import world, step + @step('I have created a Blank HTML Page$') def i_created_blank_common_problem(step): - world.create_component_instance(step, '.large-html-icon', 'i4x://edx/templates/html/Blank_HTML_Page', - '.xmodule_HtmlModule') + world.create_component_instance( + step, '.large-html-icon', 'i4x://edx/templates/html/Blank_HTML_Page', + '.xmodule_HtmlModule' + ) + @step('I see only the HTML display name setting$') def i_see_only_the_html_display_name(step): diff --git a/cms/djangoapps/contentstore/features/problem-editor.py b/cms/djangoapps/contentstore/features/problem-editor.py index 67367df477..018ede7ac6 100644 --- a/cms/djangoapps/contentstore/features/problem-editor.py +++ b/cms/djangoapps/contentstore/features/problem-editor.py @@ -5,21 +5,28 @@ from lettuce import world, step from nose.tools import assert_equal DISPLAY_NAME = "Display Name" -MAXIMUM_ATTEMPTS="Maximum Attempts" -PROBLEM_WEIGHT="Problem Weight" -RANDOMIZATION='Randomization' -SHOW_ANSWER="Show Answer" +MAXIMUM_ATTEMPTS = "Maximum Attempts" +PROBLEM_WEIGHT = "Problem Weight" +RANDOMIZATION = 'Randomization' +SHOW_ANSWER = "Show Answer" + ############### ACTIONS #################### @step('I have created a Blank Common Problem$') def i_created_blank_common_problem(step): - world.create_component_instance(step, '.large-problem-icon', 'i4x://edx/templates/problem/Blank_Common_Problem', - '.xmodule_CapaModule') + world.create_component_instance( + step, + '.large-problem-icon', + 'i4x://edx/templates/problem/Blank_Common_Problem', + '.xmodule_CapaModule' + ) + @step('I edit and select Settings$') def i_edit_and_select_settings(step): world.edit_component_and_select_settings() + @step('I see five alphabetized settings and their expected values$') def i_see_five_settings_with_values(step): world.verify_all_setting_entries( @@ -31,105 +38,124 @@ def i_see_five_settings_with_values(step): [SHOW_ANSWER, "Finished", True] ]) + @step('I can modify the display name') def i_can_modify_the_display_name(step): world.get_setting_entry(DISPLAY_NAME).find_by_css('.setting-input')[0].fill('modified') verify_modified_display_name() + @step('my display name change is persisted on save') def my_display_name_change_is_persisted_on_save(step): world.save_component_and_reopen(step) verify_modified_display_name() + @step('I can specify special characters in the display name') def i_can_modify_the_display_name_with_special_chars(step): world.get_setting_entry(DISPLAY_NAME).find_by_css('.setting-input')[0].fill("updated ' \" &") verify_modified_display_name_with_special_chars() + @step('my special characters and persisted on save') def special_chars_persisted_on_save(step): world.save_component_and_reopen(step) verify_modified_display_name_with_special_chars() + @step('I can revert the display name to unset') def can_revert_display_name_to_unset(step): world.revert_setting_entry(DISPLAY_NAME) verify_unset_display_name() + @step('my display name is unset on save') def my_display_name_is_persisted_on_save(step): world.save_component_and_reopen(step) verify_unset_display_name() + @step('I can select Per Student for Randomization') def i_can_select_per_student_for_randomization(step): world.browser.select(RANDOMIZATION, "Per Student") verify_modified_randomization() + @step('my change to randomization is persisted') def my_change_to_randomization_is_persisted(step): world.save_component_and_reopen(step) verify_modified_randomization() + @step('I can revert to the default value for randomization') def i_can_revert_to_default_for_randomization(step): world.revert_setting_entry(RANDOMIZATION) world.save_component_and_reopen(step) - world.verify_setting_entry(world.get_setting_entry(RANDOMIZATION), RANDOMIZATION, "Always", False) + world.verify_setting_entry(world.get_setting_entry(RANDOMIZATION), RANDOMIZATION, "Always", False) + @step('I can set the weight to 3.5') def i_can_set_weight_to_3_5(step): world.get_setting_entry(PROBLEM_WEIGHT).find_by_css('.setting-input')[0].fill('3.5') verify_modified_weight() + @step('my change to weight is persisted') def my_change_to_randomization_is_persisted(step): world.save_component_and_reopen(step) verify_modified_weight() + @step('I can revert to the default value of unset for weight') def i_can_revert_to_default_for_randomization(step): world.revert_setting_entry(PROBLEM_WEIGHT) world.save_component_and_reopen(step) - world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False) + world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False) + @step('if I set the weight to abc, it remains unset') def set_the_weight_to_abc(step): world.get_setting_entry(PROBLEM_WEIGHT).find_by_css('.setting-input')[0].fill('abc') # We show the clear button immediately on type, hence the "True" here. - world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", True) + world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", True) world.save_component_and_reopen(step) # But no change was actually ever sent to the model, so on reopen, explicitly_set is False - world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False) + world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False) + @step('if I set the max attempts to 2.34, the max attempts are persisted as 234') def set_the_weight_to_abc(step): world.get_setting_entry(MAXIMUM_ATTEMPTS).find_by_css('.setting-input')[0].fill('2.34') - world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, "234", True) + world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, "234", True) world.save_component_and_reopen(step) - world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, "234", True) + world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, "234", True) + @step('I set the max attempts to -3, the max attempts are persisted as 1') def set_max_attempts_to_neg_3(step): world.get_setting_entry(MAXIMUM_ATTEMPTS).find_by_css('.setting-input')[0].fill('-3') - world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, "-3", True) + world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, "-3", True) world.save_component_and_reopen(step) - world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, "1", True) + world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, "1", True) + @step('Edit High Level Source is not visible') def edit_high_level_source_not_visible(step): verify_high_level_source(step, False) + @step('Edit High Level Source is visible') def edit_high_level_source_visible(step): verify_high_level_source(step, True) + @step('If I press Cancel my changes are not persisted') def cancel_does_not_save_changes(step): world.cancel_component(step) step.given("I edit and select Settings") step.given("I see five alphabetized settings and their expected values") + @step('I have created a LaTeX Problem') def create_latex_problem(step): world.click_new_component_button(step, '.large-problem-icon') @@ -138,23 +164,28 @@ def create_latex_problem(step): world.css_click('#ui-id-2') world.click_component_from_menu("i4x://edx/templates/problem/Problem_Written_in_LaTeX", '.xmodule_CapaModule') + def verify_high_level_source(step, visible): assert_equal(visible, world.is_css_present('.launch-latex-compiler')) world.cancel_component(step) assert_equal(visible, world.is_css_present('.upload-button')) + def verify_modified_weight(): - world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "3.5", True) + world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "3.5", True) + def verify_modified_randomization(): - world.verify_setting_entry(world.get_setting_entry(RANDOMIZATION), RANDOMIZATION, "Per Student", True) + world.verify_setting_entry(world.get_setting_entry(RANDOMIZATION), RANDOMIZATION, "Per Student", True) + def verify_modified_display_name(): world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, 'modified', True) + def verify_modified_display_name_with_special_chars(): world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, "updated ' \" &", True) + def verify_unset_display_name(): world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, '', False) - diff --git a/cms/djangoapps/contentstore/features/video-editor.py b/cms/djangoapps/contentstore/features/video-editor.py index 2bd2733e59..189e933e6f 100644 --- a/cms/djangoapps/contentstore/features/video-editor.py +++ b/cms/djangoapps/contentstore/features/video-editor.py @@ -3,10 +3,14 @@ from lettuce import world, step + @step('I have created a Video component$') def i_created_a_video_component(step): - world.create_component_instance(step, '.large-video-icon', 'i4x://edx/templates/video/default', - '.xmodule_VideoModule') + world.create_component_instance( + step, '.large-video-icon', 'i4x://edx/templates/video/default', + '.xmodule_VideoModule' + ) + @step('I see only the video display name setting$') def i_see_only_the_video_display_name(step): diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 6b7bff61c1..534014d1b8 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -66,12 +66,15 @@ class ComplexEncoder(json.JSONEncoder): class CapaFields(object): attempts = StringyInteger(help="Number of attempts taken by the student on this problem", default=0, scope=Scope.user_state) - max_attempts = StringyInteger(display_name="Maximum Attempts", + max_attempts = StringyInteger( + display_name="Maximum Attempts", help="This specifies the number of times the student can try to answer this problem. If unset, infinite attempts are allowed.", - values = {"min" : 1 }, scope=Scope.settings) + values = {"min" : 1 }, scope=Scope.settings + ) due = Date(help="Date that this problem is due by", scope=Scope.settings) graceperiod = Timedelta(help="Amount of time after the due date that submissions will be accepted", scope=Scope.settings) - showanswer = String(display_name="Show Answer", + showanswer = String( + display_name="Show Answer", help="Specifies when to show the answer to this problem. A default value can be set course-wide in Advanced Settings.", scope=Scope.settings, default="closed", values=[ @@ -81,26 +84,33 @@ class CapaFields(object): {"display_name": "Closed", "value": "closed"}, {"display_name": "Finished", "value": "finished"}, {"display_name": "Past Due", "value": "past_due"}, - {"display_name": "Never", "value": "never"}]) + {"display_name": "Never", "value": "never"}] + ) force_save_button = Boolean(help="Whether to force the save button to appear on the page", scope=Scope.settings, default=False) - rerandomize = Randomization(display_name="Randomization", help="Specifies whether variable inputs for this problem are randomized each time a student loads the problem. This only applies to problems that have randomly generated numeric variables. A default value can be set course-wide in Advanced Settings.", + rerandomize = Randomization( + display_name="Randomization", help="Specifies whether variable inputs for this problem are randomized each time a student loads the problem. This only applies to problems that have randomly generated numeric variables. A default value can be set course-wide in Advanced Settings.", default="always", scope=Scope.settings, values=[{"display_name": "Always", "value": "always"}, {"display_name": "On Reset", "value": "onreset"}, {"display_name": "Never", "value": "never"}, - {"display_name": "Per Student", "value": "per_student"}]) + {"display_name": "Per Student", "value": "per_student"}] + ) data = String(help="XML data for the problem", scope=Scope.content) correct_map = Object(help="Dictionary with the correctness of current student answers", scope=Scope.user_state, default={}) input_state = Object(help="Dictionary for maintaining the state of inputtypes", scope=Scope.user_state) student_answers = Object(help="Dictionary with the current student responses", scope=Scope.user_state) done = Boolean(help="Whether the student has answered the problem", scope=Scope.user_state) seed = StringyInteger(help="Random seed for this student", scope=Scope.user_state) - weight = StringyFloat(display_name="Problem Weight", + weight = StringyFloat( + display_name="Problem Weight", help="Specifies the number of points the problem is worth. If unset, each response field in the problem is worth one point.", values = {"min" : 0 , "step": .1}, - scope=Scope.settings) + scope=Scope.settings + ) markdown = String(help="Markdown source of this module", scope=Scope.settings) - source_code = String(help="Source code for LaTeX and Word problems. This feature is not well-supported.", - scope=Scope.settings) + source_code = String( + help="Source code for LaTeX and Word problems. This feature is not well-supported.", + scope=Scope.settings + ) class CapaModule(CapaFields, XModule): diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index d8d129a3a1..39d5de9489 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -55,26 +55,38 @@ class CombinedOpenEndedFields(object): scope=Scope.user_state) student_attempts = StringyInteger(help="Number of attempts taken by the student on this problem", default=0, scope=Scope.user_state) - ready_to_reset = StringyBoolean(help="If the problem is ready to be reset or not.", default=False, - scope=Scope.user_state) - attempts = StringyInteger(display_name="Maximum Attempts", + ready_to_reset = StringyBoolean( + help="If the problem is ready to be reset or not.", default=False, + scope=Scope.user_state + ) + attempts = StringyInteger( + display_name="Maximum Attempts", help="The number of times the student can try to answer this problem.", default=1, - scope=Scope.settings, values = {"min" : 1 }) + scope=Scope.settings, values = {"min" : 1 } + ) is_graded = StringyBoolean(display_name="Graded", help="Whether or not the problem is graded.", default=False, scope=Scope.settings) - accept_file_upload = StringyBoolean(display_name="Allow File Uploads", - help="Whether or not the student can submit files as a response.", default=False, scope=Scope.settings) - skip_spelling_checks = StringyBoolean(display_name="Disable Quality Filter", - # TODO: passing of text failed with "won't". Need to make our code more robust. - help="If False, submissions with poor spelling, short length, or poor grammar will not be peer reviewed.", - default=False, scope=Scope.settings) + accept_file_upload = StringyBoolean( + display_name="Allow File Uploads", + help="Whether or not the student can submit files as a response.", default=False, scope=Scope.settings + ) + skip_spelling_checks = StringyBoolean( + display_name="Disable Quality Filter", + help="If False, the Quality Filter is enabled and submissions with poor spelling, short length, or poor grammar will not be peer reviewed.", + default=False, scope=Scope.settings + ) due = Date(help="Date that this problem is due by", default=None, scope=Scope.settings) - graceperiod = String(help="Amount of time after the due date that submissions will be accepted", default=None, - scope=Scope.settings) + graceperiod = String( + help="Amount of time after the due date that submissions will be accepted", + default=None, + scope=Scope.settings + ) version = VersionInteger(help="Current version number", default=DEFAULT_VERSION, scope=Scope.settings) data = String(help="XML data for the problem", scope=Scope.content) - weight = StringyFloat(display_name="Problem Weight", + weight = StringyFloat( + display_name="Problem Weight", help="The number of points the problem is worth. By default, each problem is worth one point.", - scope=Scope.settings, values = {"min" : 0 , "step": ".1"}) + scope=Scope.settings, values = {"min" : 0 , "step": ".1"} + ) markdown = String(help="Markdown source of this module", scope=Scope.settings) diff --git a/common/lib/xmodule/xmodule/discussion_module.py b/common/lib/xmodule/xmodule/discussion_module.py index 62d14e0600..390f2fc18a 100644 --- a/common/lib/xmodule/xmodule/discussion_module.py +++ b/common/lib/xmodule/xmodule/discussion_module.py @@ -8,12 +8,16 @@ from xblock.core import String, Scope class DiscussionFields(object): discussion_id = String(scope=Scope.settings) - discussion_category = String(display_name="Category", + discussion_category = String( + display_name="Category", help="Specifies a category name for this discussion. This name appears in the left pane of the discussion forum for your course.", - scope=Scope.settings) - discussion_target = String(display_name="Subcategory", + scope=Scope.settings + ) + discussion_target = String( + display_name="Subcategory", help="Specifies a subcategory name for this discussion. This name appears in the left pane of the discussion forum for your course.", - scope=Scope.settings) + scope=Scope.settings + ) sort_key = String(scope=Scope.settings) diff --git a/common/lib/xmodule/xmodule/peer_grading_module.py b/common/lib/xmodule/xmodule/peer_grading_module.py index 0ff766d328..4fd768d63b 100644 --- a/common/lib/xmodule/xmodule/peer_grading_module.py +++ b/common/lib/xmodule/xmodule/peer_grading_module.py @@ -28,25 +28,37 @@ EXTERNAL_GRADER_NO_CONTACT_ERROR = "Failed to contact external graders. Please class PeerGradingFields(object): - use_for_single_location = StringyBoolean(display_name="Show Single Problem", + use_for_single_location = StringyBoolean( + display_name="Show Single Problem", help='When True, only the single problem specified by "Link to Problem Location" is shown. ' 'When False, a panel is displayed with all problems available for peer grading.', - default=USE_FOR_SINGLE_LOCATION, scope=Scope.settings) - link_to_location = String(display_name="Link to Problem Location", + default=USE_FOR_SINGLE_LOCATION, scope=Scope.settings + ) + link_to_location = String( + display_name="Link to Problem Location", help='The location of the problem being graded. Only used when "Show Single Problem" is True.', - default=LINK_TO_LOCATION, scope=Scope.settings) - is_graded = StringyBoolean(display_name="Graded", - help='Whether the student gets credit for grading this problem. Only used when "Show Single Problem" is True.', - default=IS_GRADED, scope=Scope.settings) + default=LINK_TO_LOCATION, scope=Scope.settings + ) + is_graded = StringyBoolean( + display_name="Graded", + help='Defines whether the student gets credit for grading this problem. Only used when "Show Single Problem" is True.', + default=IS_GRADED, scope=Scope.settings + ) due_date = Date(help="Due date that should be displayed.", default=None, scope=Scope.settings) grace_period_string = String(help="Amount of grace to give on the due date.", default=None, scope=Scope.settings) - max_grade = StringyInteger(help="The maximum grade that a student can receive for this problem.", default=MAX_SCORE, - scope=Scope.settings, values={"min" : 0 }) - student_data_for_location = Object(help="Student data for a given peer grading problem.", - scope=Scope.user_state) - weight = StringyFloat(display_name="Problem Weight", + max_grade = StringyInteger( + help="The maximum grade that a student can receive for this problem.", default=MAX_SCORE, + scope=Scope.settings, values={"min": 0} + ) + student_data_for_location = Object( + help="Student data for a given peer grading problem.", + scope=Scope.user_state + ) + weight = StringyFloat( + display_name="Problem Weight", help="Specifies the number of points the problem is worth. By default, each problem is worth one point.", - scope=Scope.settings, values = {"min" : 0 , "step": ".1"}) + scope=Scope.settings, values={"min": 0, "step": ".1"} + ) class PeerGradingModule(PeerGradingFields, XModule): @@ -299,14 +311,14 @@ class PeerGradingModule(PeerGradingFields, XModule): try: response = self.peer_gs.save_grade(location, grader_id, submission_id, - score, feedback, submission_key, rubric_scores, submission_flagged) + score, feedback, submission_key, rubric_scores, submission_flagged) return response except GradingServiceError: #This is a dev_facing_error log.exception("""Error saving grade to open ended grading service. server url: {0}, location: {1}, submission_id:{2}, submission_key: {3}, score: {4}""" .format(self.peer_gs.url, - location, submission_id, submission_key, score) + location, submission_id, submission_key, score) ) #This is a student_facing_error return { @@ -437,7 +449,7 @@ class PeerGradingModule(PeerGradingFields, XModule): try: response = self.peer_gs.save_calibration_essay(location, grader_id, calibration_essay_id, - submission_key, score, feedback, rubric_scores) + submission_key, score, feedback, rubric_scores) if 'actual_rubric' in response: rubric_renderer = combined_open_ended_rubric.CombinedOpenEndedRubric(self.system, True) response['actual_rubric'] = rubric_renderer.render_rubric(response['actual_rubric'])['html'] diff --git a/common/lib/xmodule/xmodule/tests/test_xml_module.py b/common/lib/xmodule/xmodule/tests/test_xml_module.py index 2278672564..8f302194a6 100644 --- a/common/lib/xmodule/xmodule/tests/test_xml_module.py +++ b/common/lib/xmodule/xmodule/tests/test_xml_module.py @@ -1,88 +1,106 @@ +#pylint: disable=C0111 +#pylint: disable=W0621 + from xmodule.x_module import XModuleFields from xblock.core import Scope, String, Object, Boolean from xmodule.fields import Date, StringyInteger, StringyFloat from xmodule.xml_module import XmlDescriptor import unittest -from . import test_system +from .import test_system from mock import Mock class CrazyJsonString(String): - def to_json(self, value): return value + " JSON" class TestFields(object): # Will be returned by editable_metadata_fields. - max_attempts = StringyInteger(scope=Scope.settings, default=1000, values={'min': 1 , 'max' : 10}) + max_attempts = StringyInteger(scope=Scope.settings, default=1000, values={'min': 1, 'max': 10}) # Will not be returned by editable_metadata_fields because filtered out by non_editable_metadata_fields. due = Date(scope=Scope.settings) # Will not be returned by editable_metadata_fields because is not Scope.settings. student_answers = Object(scope=Scope.user_state) # Will be returned, and can override the inherited value from XModule. - display_name = String(scope=Scope.settings, default='local default', display_name = 'Local Display Name', - help='local help') + display_name = String(scope=Scope.settings, default='local default', display_name='Local Display Name', + help='local help') # Used for testing select type, effect of to_json method - string_select = CrazyJsonString(scope=Scope.settings, default='default value', - values=[{'display_name' : 'first', 'value' : 'value a'}, - {'display_name' : 'second','value' : 'value b'}]) + string_select = CrazyJsonString( + scope=Scope.settings, + default='default value', + values=[{'display_name': 'first', 'value': 'value a'}, + {'display_name': 'second', 'value': 'value b'}] + ) # Used for testing select type float_select = StringyFloat(scope=Scope.settings, default=.999, values=[1.23, 0.98]) # Used for testing float type - float_non_select = StringyFloat(scope=Scope.settings, default=.999, values={'min': 0 , 'step' : .3}) + float_non_select = StringyFloat(scope=Scope.settings, default=.999, values={'min': 0, 'step': .3}) # Used for testing that Booleans get mapped to select type boolean_select = Boolean(scope=Scope.settings) class EditableMetadataFieldsTest(unittest.TestCase): - def test_display_name_field(self): editable_fields = self.get_xml_editable_fields({}) # Tests that the xblock fields (currently tags and name) get filtered out. # Also tests that xml_attributes is filtered out of XmlDescriptor. self.assertEqual(1, len(editable_fields), "Expected only 1 editable field for xml descriptor.") - self.assert_field_values(editable_fields, 'display_name', XModuleFields.display_name, - explicitly_set=False, inheritable=False, value=None, default_value=None) + self.assert_field_values( + editable_fields, 'display_name', XModuleFields.display_name, + explicitly_set=False, inheritable=False, value=None, default_value=None + ) def test_override_default(self): # Tests that explicitly_set is correct when a value overrides the default (not inheritable). editable_fields = self.get_xml_editable_fields({'display_name': 'foo'}) - self.assert_field_values(editable_fields, 'display_name', XModuleFields.display_name, - explicitly_set=True, inheritable=False, value='foo', default_value=None) + self.assert_field_values( + editable_fields, 'display_name', XModuleFields.display_name, + explicitly_set=True, inheritable=False, value='foo', default_value=None + ) def test_integer_field(self): - descriptor = self.get_descriptor({'max_attempts' : '7'}) + descriptor = self.get_descriptor({'max_attempts': '7'}) editable_fields = descriptor.editable_metadata_fields self.assertEqual(6, len(editable_fields)) - self.assert_field_values(editable_fields, 'max_attempts', TestFields.max_attempts, + self.assert_field_values( + editable_fields, 'max_attempts', TestFields.max_attempts, explicitly_set=True, inheritable=False, value=7, default_value=1000, type='Integer', - options=TestFields.max_attempts.values) - self.assert_field_values(editable_fields, 'display_name', TestFields.display_name, - explicitly_set=False, inheritable=False, value='local default', default_value='local default') + options=TestFields.max_attempts.values + ) + self.assert_field_values( + editable_fields, 'display_name', TestFields.display_name, + explicitly_set=False, inheritable=False, value='local default', default_value='local default' + ) editable_fields = self.get_descriptor({}).editable_metadata_fields - self.assert_field_values(editable_fields, 'max_attempts', TestFields.max_attempts, + self.assert_field_values( + editable_fields, 'max_attempts', TestFields.max_attempts, explicitly_set=False, inheritable=False, value=1000, default_value=1000, type='Integer', - options=TestFields.max_attempts.values) + options=TestFields.max_attempts.values + ) def test_inherited_field(self): - model_val = {'display_name' : 'inherited'} + model_val = {'display_name': 'inherited'} descriptor = self.get_descriptor(model_val) # Mimic an inherited value for display_name (inherited and inheritable are the same in this case). descriptor._inherited_metadata = model_val descriptor._inheritable_metadata = model_val editable_fields = descriptor.editable_metadata_fields - self.assert_field_values(editable_fields, 'display_name', TestFields.display_name, - explicitly_set=False, inheritable=True, value='inherited', default_value='inherited') + self.assert_field_values( + editable_fields, 'display_name', TestFields.display_name, + explicitly_set=False, inheritable=True, value='inherited', default_value='inherited' + ) - descriptor = self.get_descriptor({'display_name' : 'explicit'}) + descriptor = self.get_descriptor({'display_name': 'explicit'}) # Mimic the case where display_name WOULD have been inherited, except we explicitly set it. - descriptor._inheritable_metadata = {'display_name' : 'inheritable value'} + descriptor._inheritable_metadata = {'display_name': 'inheritable value'} descriptor._inherited_metadata = {} editable_fields = descriptor.editable_metadata_fields - self.assert_field_values(editable_fields, 'display_name', TestFields.display_name, - explicitly_set=True, inheritable=True, value='explicit', default_value='inheritable value') + self.assert_field_values( + editable_fields, 'display_name', TestFields.display_name, + explicitly_set=True, inheritable=True, value='explicit', default_value='inheritable value' + ) def test_type_and_options(self): # test_display_name_field verifies that a String field is of type "Generic". @@ -92,23 +110,31 @@ class EditableMetadataFieldsTest(unittest.TestCase): editable_fields = descriptor.editable_metadata_fields # Tests for select - self.assert_field_values(editable_fields, 'string_select', TestFields.string_select, + self.assert_field_values( + editable_fields, 'string_select', TestFields.string_select, explicitly_set=False, inheritable=False, value='default value', default_value='default value', - type='Select', options=[{'display_name' : 'first', 'value' : 'value a JSON'}, - {'display_name' : 'second','value' : 'value b JSON'}]) + type='Select', options=[{'display_name': 'first', 'value': 'value a JSON'}, + {'display_name': 'second', 'value': 'value b JSON'}] + ) - self.assert_field_values(editable_fields, 'float_select', TestFields.float_select, + self.assert_field_values( + editable_fields, 'float_select', TestFields.float_select, explicitly_set=False, inheritable=False, value=.999, default_value=.999, - type='Select', options=[1.23, 0.98]) + type='Select', options=[1.23, 0.98] + ) - self.assert_field_values(editable_fields, 'boolean_select', TestFields.boolean_select, + self.assert_field_values( + editable_fields, 'boolean_select', TestFields.boolean_select, explicitly_set=False, inheritable=False, value=None, default_value=None, - type='Select', options=[{'display_name': "True", "value": True}, {'display_name': "False", "value": False}]) + type='Select', options=[{'display_name': "True", "value": True}, {'display_name': "False", "value": False}] + ) # Test for float - self.assert_field_values(editable_fields, 'float_non_select', TestFields.float_non_select, + self.assert_field_values( + editable_fields, 'float_non_select', TestFields.float_non_select, explicitly_set=False, inheritable=False, value=.999, default_value=.999, - type='Float', options={'min': 0 , 'step' : .3}) + type='Float', options={'min': 0, 'step': .3} + ) # Start of helper methods @@ -119,7 +145,6 @@ class EditableMetadataFieldsTest(unittest.TestCase): def get_descriptor(self, model_data): class TestModuleDescriptor(TestFields, XmlDescriptor): - @property def non_editable_metadata_fields(self): non_editable_fields = super(TestModuleDescriptor, self).non_editable_metadata_fields diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index d398f8fc2f..31b2947106 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -82,7 +82,7 @@ class HTMLSnippet(object): class XModuleFields(object): display_name = String( display_name="Display Name", - help="Specifies the name for this component. The name appears as a tooltip in the course ribbon at the top of the page.", + help="Specifies the name for this component. The name appears in the course navigation at the top of the page.", scope=Scope.settings, default=None )