From 94e41319ee5d50b9ee9b985ac51a4ea895994ddb Mon Sep 17 00:00:00 2001 From: cahrens Date: Wed, 1 May 2013 09:09:33 -0400 Subject: [PATCH] Unit test, remove display_name for now so there is no visible change. --- common/lib/xmodule/xmodule/capa_module.py | 21 +++--- .../lib/xmodule/xmodule/discussion_module.py | 11 +-- common/lib/xmodule/xmodule/fields.py | 9 ++- common/lib/xmodule/xmodule/mako_module.py | 3 +- .../xmodule/xmodule/tests/test_mako_module.py | 73 +++++++++++++++++++ common/lib/xmodule/xmodule/x_module.py | 6 -- common/lib/xmodule/xmodule/xml_module.py | 4 +- 7 files changed, 100 insertions(+), 27 deletions(-) create mode 100644 common/lib/xmodule/xmodule/tests/test_mako_module.py diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index bc5014d75b..015bce4e85 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -17,7 +17,7 @@ from xmodule.x_module import XModule, XModuleFields from xmodule.raw_module import RawDescriptor from xmodule.exceptions import NotFoundError, ProcessingError from xblock.core import Scope, String, Boolean, Object -from .fields import Timedelta, Date, StringyInteger, StringyFloat +from .fields import Timedelta, Date, StringyInteger, StringyFloat, NON_EDITABLE_SETTINGS_SCOPE from xmodule.util.date_utils import time_to_datetime log = logging.getLogger("mitx.courseware") @@ -62,26 +62,23 @@ 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 Allowed Attempts", - help="Maximum number of attempts that a student is allowed", scope=Scope.settings) - due = Date(help="Date that this problem is due by", scope=XModuleFields.nonEditableSettingsScope) + max_attempts = StringyInteger(help="Maximum number of attempts that a student is allowed", scope=Scope.settings) + due = Date(help="Date that this problem is due by", scope=NON_EDITABLE_SETTINGS_SCOPE) graceperiod = Timedelta(help="Amount of time after the due date that submissions will be accepted", - scope=XModuleFields.nonEditableSettingsScope) - showanswer = String(display_name="Show Answer", - help="When to show the problem answer to the student", scope=Scope.settings, default="closed", + scope=NON_EDITABLE_SETTINGS_SCOPE) + showanswer = String(help="When to show the problem answer to the student", scope=Scope.settings, default="closed", values=["answered", "always", "attempted", "closed", "never"]) force_save_button = Boolean(help="Whether to force the save button to appear on the page", - scope=XModuleFields.nonEditableSettingsScope, default=False) - rerandomize = Randomization(display_name="Rerandomize", help="When to rerandomize the problem", - default="always", scope=Scope.settings) + scope=NON_EDITABLE_SETTINGS_SCOPE, default=False) + rerandomize = Randomization(help="When to rerandomize the problem", default="always", scope=Scope.settings) 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", help="How much to weight this problem by", scope=Scope.settings) - markdown = String(help="Markdown source of this module", scope=XModuleFields.nonEditableSettingsScope) + weight = StringyFloat(help="How much to weight this problem by", scope=Scope.settings) + markdown = String(help="Markdown source of this module", scope=NON_EDITABLE_SETTINGS_SCOPE) source_code = String(help="Source code for LaTeX and Word problems. This feature is not well-supported.", scope=Scope.settings) diff --git a/common/lib/xmodule/xmodule/discussion_module.py b/common/lib/xmodule/xmodule/discussion_module.py index 9c2681ef2d..b4409f5216 100644 --- a/common/lib/xmodule/xmodule/discussion_module.py +++ b/common/lib/xmodule/xmodule/discussion_module.py @@ -1,17 +1,18 @@ from pkg_resources import resource_string -from xmodule.x_module import XModule, XModuleFields +from .fields import NON_EDITABLE_SETTINGS_SCOPE +from xmodule.x_module import XModule from xmodule.raw_module import RawDescriptor from xmodule.editing_module import MetadataOnlyEditingDescriptor from xblock.core import String, Scope class DiscussionFields(object): - discussion_id = String(scope=XModuleFields.nonEditableSettingsScope) - discussion_category = String(display_name="Category Name", scope=Scope.settings) - discussion_target = String(display_name="Subcategory Name", scope=Scope.settings) + discussion_id = String(scope=NON_EDITABLE_SETTINGS_SCOPE) + discussion_category = String(scope=Scope.settings) + discussion_target = String(scope=Scope.settings) # We may choose to enable this in the future, but while Kevin is investigating.... - sort_key = String(scope=XModuleFields.nonEditableSettingsScope) + sort_key = String(scope=NON_EDITABLE_SETTINGS_SCOPE) class DiscussionModule(DiscussionFields, XModule): diff --git a/common/lib/xmodule/xmodule/fields.py b/common/lib/xmodule/xmodule/fields.py index 3d56b7941e..b79accb2b3 100644 --- a/common/lib/xmodule/xmodule/fields.py +++ b/common/lib/xmodule/xmodule/fields.py @@ -7,11 +7,18 @@ from xblock.core import ModelType import datetime import dateutil.parser -from xblock.core import Integer, Float, Boolean +from xblock.core import Integer, Float, Boolean, Scope log = logging.getLogger(__name__) +class NonEditableSettingsScope(Scope): + pass + +# Same scope as Settings.scope, but not intended to be edited by users (in Studio). +NON_EDITABLE_SETTINGS_SCOPE = NonEditableSettingsScope(user=Scope.settings.user, block=Scope.settings.block) + + class Date(ModelType): ''' Date fields know how to parse and produce json (iso) compatible formats. diff --git a/common/lib/xmodule/xmodule/mako_module.py b/common/lib/xmodule/xmodule/mako_module.py index e01a03e309..02db0c602f 100644 --- a/common/lib/xmodule/xmodule/mako_module.py +++ b/common/lib/xmodule/xmodule/mako_module.py @@ -1,4 +1,5 @@ -from .x_module import XModuleDescriptor, DescriptorSystem, NonEditableSettingsScope +from .x_module import XModuleDescriptor, DescriptorSystem +from .fields import NonEditableSettingsScope from xblock.core import Scope from xblock.core import XBlock diff --git a/common/lib/xmodule/xmodule/tests/test_mako_module.py b/common/lib/xmodule/xmodule/tests/test_mako_module.py new file mode 100644 index 0000000000..7686e2a69e --- /dev/null +++ b/common/lib/xmodule/xmodule/tests/test_mako_module.py @@ -0,0 +1,73 @@ +from xmodule.x_module import XModuleFields +from xblock.core import Scope, String, Object +from xmodule.fields import Date, StringyInteger, NON_EDITABLE_SETTINGS_SCOPE +from xmodule.mako_module import MakoModuleDescriptor +import unittest +from . import test_system +from mock import Mock + + +class TestFields(object): + # Will be returned by editable_metadata_fields because Scope.settings. + max_attempts = StringyInteger(scope=Scope.settings) + # Will not be returned by editable_metadata_fields because declared as non-editable Scope.settings. + due = Date(scope=NON_EDITABLE_SETTINGS_SCOPE) + # 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) + + +class EditableMetadataFieldsTest(unittest.TestCase): + + def test_display_name_field(self): + editable_fields = self.get_mako_editable_fields({}) + # Tests that the xblock fields (currently tags and name) get filtered out. + self.assertEqual(1, len(editable_fields), "Expected only 1 editable field for mako descriptor.") + self.assert_display_name_default(editable_fields) + + def test_override_default(self): + # Tests that is_default is correct when a value overrides the default. + editable_fields = self.get_mako_editable_fields({'display_name': 'foo'}) + display_name = editable_fields['display_name'] + self.assertFalse(display_name['is_default']) + self.assertEqual('foo', display_name['value']) + + def test_additional_field(self): + editable_fields = self.get_module_editable_fields({'max_attempts' : '7'}) + self.assertEqual(2, len(editable_fields)) + self.assert_field_values(editable_fields, 'max_attempts', TestFields.max_attempts, False, False, 7) + self.assert_display_name_default(editable_fields) + + editable_fields = self.get_module_editable_fields({}) + self.assert_field_values(editable_fields, 'max_attempts', TestFields.max_attempts, True, False, None) + + def test_inherited_field(self): + editable_fields = self.get_module_editable_fields({'display_name' : 'inherited'}) + self.assert_field_values(editable_fields, 'display_name', XModuleFields.display_name, False, True, 'inherited') + + # Start of helper methods + def get_mako_editable_fields(self, model_data): + system = test_system() + system.render_template = Mock(return_value="
Test Template HTML
") + return MakoModuleDescriptor(system=system, location=None, model_data=model_data).editable_metadata_fields + + def get_module_editable_fields(self, model_data): + class TestModuleDescriptor(TestFields, MakoModuleDescriptor): + pass + + system = test_system() + system.render_template = Mock(return_value="
Test Template HTML
") + descriptor = TestModuleDescriptor(system=system, location=None, model_data=model_data) + descriptor._inherited_metadata = {'display_name' : 'inherited'} + return descriptor.editable_metadata_fields + + def assert_display_name_default(self, editable_fields): + self.assert_field_values(editable_fields, 'display_name', XModuleFields.display_name, True, False, None) + + def assert_field_values(self, editable_fields, name, field, is_default, is_inherited, value): + test_field = editable_fields[name] + self.assertEqual(field, test_field['field']) + self.assertEqual(is_default, test_field['is_default']) + self.assertEqual(is_inherited, test_field['is_inherited']) + self.assertEqual(value, test_field['value']) diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index 4a16548c6f..04ffcc6092 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -78,18 +78,12 @@ class HTMLSnippet(object): .format(self.__class__)) -class NonEditableSettingsScope(Scope): - pass - - class XModuleFields(object): display_name = String( - display_name="Display Name", help="Display name for this module", scope=Scope.settings, default=None ) - nonEditableSettingsScope = NonEditableSettingsScope(user=Scope.settings.user, block=Scope.settings.block) class XModule(XModuleFields, HTMLSnippet, XBlock): diff --git a/common/lib/xmodule/xmodule/xml_module.py b/common/lib/xmodule/xmodule/xml_module.py index bfbf12635a..a34b0e5078 100644 --- a/common/lib/xmodule/xmodule/xml_module.py +++ b/common/lib/xmodule/xmodule/xml_module.py @@ -10,7 +10,7 @@ from xblock.core import Object from xmodule.x_module import (XModuleDescriptor, policy_key) from xmodule.modulestore import Location from xmodule.modulestore.inheritance import own_metadata -from xmodule.x_module import XModuleFields +from .fields import NON_EDITABLE_SETTINGS_SCOPE log = logging.getLogger(__name__) @@ -86,7 +86,7 @@ class XmlDescriptor(XModuleDescriptor): """ xml_attributes = Object(help="Map of unhandled xml attributes, used only for storage between import and export", - default={}, scope=XModuleFields.nonEditableSettingsScope) + default={}, scope=NON_EDITABLE_SETTINGS_SCOPE) # Extension to append to filename paths filename_extension = 'xml'