Commit 37d594ce by cahrens

Get rid of non-editable scope.

parent 0a1902e7
......@@ -17,7 +17,7 @@ from xmodule.x_module import XModule
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, NON_EDITABLE_SETTINGS_SCOPE
from .fields import Timedelta, Date, StringyInteger, StringyFloat
from xmodule.util.date_utils import time_to_datetime
log = logging.getLogger("mitx.courseware")
......@@ -63,13 +63,13 @@ 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(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)
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=NON_EDITABLE_SETTINGS_SCOPE)
scope=Scope.settings)
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=NON_EDITABLE_SETTINGS_SCOPE, default=False)
scope=Scope.settings, 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={})
......@@ -78,7 +78,7 @@ class CapaFields(object):
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(help="How much to weight this problem by", scope=Scope.settings)
markdown = String(help="Markdown source of this module", scope=NON_EDITABLE_SETTINGS_SCOPE)
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)
......@@ -894,3 +894,10 @@ class CapaDescriptor(CapaFields, RawDescriptor):
'problems/' + path[8:],
path[8:],
]
@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])
return non_editable_fields
\ No newline at end of file
from pkg_resources import resource_string
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
......@@ -8,11 +7,10 @@ from xblock.core import String, Scope
class DiscussionFields(object):
discussion_id = String(scope=NON_EDITABLE_SETTINGS_SCOPE)
discussion_id = String(scope=Scope.settings)
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=NON_EDITABLE_SETTINGS_SCOPE)
sort_key = String(scope=Scope.settings)
class DiscussionModule(DiscussionFields, XModule):
......@@ -39,3 +37,10 @@ class DiscussionDescriptor(DiscussionFields, MetadataOnlyEditingDescriptor, RawD
metadata_translations = dict(RawDescriptor.metadata_translations)
metadata_translations['id'] = 'discussion_id'
metadata_translations['for'] = 'discussion_target'
@property
def non_editable_metadata_fields(self):
non_editable_fields = super(DiscussionDescriptor, self).non_editable_metadata_fields
# We may choose to enable sort_keys in the future, but while Kevin is investigating....
non_editable_fields.extend([DiscussionDescriptor.discussion_id, DiscussionDescriptor.sort_key])
return non_editable_fields
\ No newline at end of file
......@@ -7,18 +7,11 @@ from xblock.core import ModelType
import datetime
import dateutil.parser
from xblock.core import Integer, Float, Boolean, Scope
from xblock.core import Integer, Float, Boolean
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.
......
from .x_module import XModuleDescriptor, DescriptorSystem
from .fields import NonEditableSettingsScope
from xblock.core import Scope
from xblock.core import XBlock
class MakoDescriptorSystem(DescriptorSystem):
......@@ -43,33 +40,3 @@ class MakoModuleDescriptor(XModuleDescriptor):
return self.system.render_template(
self.mako_template, self.get_context())
@property
def editable_metadata_fields(self):
inherited_metadata = getattr(self, '_inherited_metadata', {})
metadata = {}
for field in self.fields:
if field.scope != Scope.settings or isinstance(field.scope, NonEditableSettingsScope):
continue
# We are not allowing editing of xblock tag and name fields at this time (for any component).
if field == XBlock.tags or field == XBlock.name:
continue
inherited = False
default = False
value = getattr(self, field.name)
if field.name in self._model_data:
default = False
if field.name in inherited_metadata and self._model_data.get(field.name) == inherited_metadata.get(
field.name):
inherited = True
else:
default = True
metadata[field.name] = {'field' : field,
'value': value,
'is_inherited': inherited,
'is_default': default }
return metadata
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
from xmodule.fields import Date, StringyInteger
from xmodule.xml_module import XmlDescriptor
import unittest
from . import test_system
from mock import Mock
class TestFields(object):
# Will be returned by editable_metadata_fields because Scope.settings.
# Will be returned by editable_metadata_fields.
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 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.
......@@ -21,14 +21,15 @@ class TestFields(object):
class EditableMetadataFieldsTest(unittest.TestCase):
def test_display_name_field(self):
editable_fields = self.get_mako_editable_fields({})
editable_fields = self.get_xml_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.")
# 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_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'})
editable_fields = self.get_xml_editable_fields({'display_name': 'foo'})
display_name = editable_fields['display_name']
self.assertFalse(display_name['is_default'])
self.assertEqual('foo', display_name['value'])
......@@ -47,14 +48,19 @@ class EditableMetadataFieldsTest(unittest.TestCase):
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):
def get_xml_editable_fields(self, model_data):
system = test_system()
system.render_template = Mock(return_value="<div>Test Template HTML</div>")
return MakoModuleDescriptor(system=system, location=None, model_data=model_data).editable_metadata_fields
return XmlDescriptor(system=system, location=None, model_data=model_data).editable_metadata_fields
def get_module_editable_fields(self, model_data):
class TestModuleDescriptor(TestFields, MakoModuleDescriptor):
pass
class TestModuleDescriptor(TestFields, XmlDescriptor):
@property
def non_editable_metadata_fields(self):
non_editable_fields = super(TestModuleDescriptor, self).non_editable_metadata_fields
non_editable_fields.append(TestModuleDescriptor.due)
return non_editable_fields
system = test_system()
system.render_template = Mock(return_value="<div>Test Template HTML</div>")
......
......@@ -606,6 +606,48 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
model_data=self._model_data,
))
@property
def non_editable_metadata_fields(self):
"""
Return the list of fields that should not be editable in Studio.
When overriding, be sure to append to the superclasses' list.
"""
# We are not allowing editing of xblock tag and name fields at this time (for any component).
return [XBlock.tags, XBlock.name]
@property
def editable_metadata_fields(self):
"""
Returns the metadata fields to be edited in Studio. These are fields with scope `Scope.settings`.
Can be limited by extending `non_editable_metadata_fields`.
"""
inherited_metadata = getattr(self, '_inherited_metadata', {})
metadata = {}
for field in self.fields:
if field.scope != Scope.settings or field in self.non_editable_metadata_fields:
continue
inherited = False
default = False
value = getattr(self, field.name)
if field.name in self._model_data:
default = False
if field.name in inherited_metadata:
if self._model_data.get(field.name) == inherited_metadata.get(field.name):
inherited = True
else:
default = True
metadata[field.name] = {'field': field,
'value': value,
'is_inherited': inherited,
'is_default': default}
return metadata
class DescriptorSystem(object):
def __init__(self, load_item, resources_fs, error_tracker, **kwargs):
......
......@@ -6,11 +6,10 @@ import sys
from collections import namedtuple
from lxml import etree
from xblock.core import Object
from xblock.core import Object, Scope
from xmodule.x_module import (XModuleDescriptor, policy_key)
from xmodule.modulestore import Location
from xmodule.modulestore.inheritance import own_metadata
from .fields import NON_EDITABLE_SETTINGS_SCOPE
log = logging.getLogger(__name__)
......@@ -86,7 +85,7 @@ class XmlDescriptor(XModuleDescriptor):
"""
xml_attributes = Object(help="Map of unhandled xml attributes, used only for storage between import and export",
default={}, scope=NON_EDITABLE_SETTINGS_SCOPE)
default={}, scope=Scope.settings)
# Extension to append to filename paths
filename_extension = 'xml'
......@@ -420,3 +419,9 @@ class XmlDescriptor(XModuleDescriptor):
"""
raise NotImplementedError(
"%s does not implement definition_to_xml" % self.__class__.__name__)
@property
def non_editable_metadata_fields(self):
non_editable_fields = super(XmlDescriptor, self).non_editable_metadata_fields
non_editable_fields.append(XmlDescriptor.xml_attributes)
return non_editable_fields
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment