diff --git a/common/lib/xmodule/xmodule/abtest_module.py b/common/lib/xmodule/xmodule/abtest_module.py index e18586726c..0e1c66df8e 100644 --- a/common/lib/xmodule/xmodule/abtest_module.py +++ b/common/lib/xmodule/xmodule/abtest_module.py @@ -31,17 +31,19 @@ def group_from_value(groups, v): return g -class ABTestModule(XModule): - """ - Implements an A/B test with an aribtrary number of competing groups - """ - +class ABTestFields(object): group_portions = Object(help="What proportions of students should go in each group", default={DEFAULT: 1}, scope=Scope.content) group_assignments = Object(help="What group this user belongs to", scope=Scope.student_preferences, default={}) group_content = Object(help="What content to display to each group", scope=Scope.content, default={DEFAULT: []}) experiment = String(help="Experiment that this A/B test belongs to", scope=Scope.content) has_children = True + +class ABTestModule(ABTestFields, XModule): + """ + Implements an A/B test with an aribtrary number of competing groups + """ + def __init__(self, *args, **kwargs): XModule.__init__(self, *args, **kwargs) @@ -75,16 +77,11 @@ class ABTestModule(XModule): # TODO (cpennington): Use Groups should be a first class object, rather than being # managed by ABTests -class ABTestDescriptor(RawDescriptor, XmlDescriptor): +class ABTestDescriptor(ABTestFields, RawDescriptor, XmlDescriptor): module_class = ABTestModule template_dir_name = "abtest" - experiment = String(help="Experiment that this A/B test belongs to", scope=Scope.content) - group_portions = Object(help="What proportions of students should go in each group", default={}) - group_content = Object(help="What content to display to each group", scope=Scope.content, default={DEFAULT: []}) - has_children = True - @classmethod def definition_from_xml(cls, xml_object, system): """ diff --git a/common/lib/xmodule/xmodule/annotatable_module.py b/common/lib/xmodule/xmodule/annotatable_module.py index ceeef6d004..5b167dd0be 100644 --- a/common/lib/xmodule/xmodule/annotatable_module.py +++ b/common/lib/xmodule/xmodule/annotatable_module.py @@ -12,7 +12,12 @@ from xblock.core import Scope, String log = logging.getLogger(__name__) -class AnnotatableModule(XModule): + +class AnnotatableFields(object): + data = String(help="XML data for the annotation", scope=Scope.content) + + +class AnnotatableModule(AnnotatableFields, XModule): js = {'coffee': [resource_string(__name__, 'js/src/javascript_loader.coffee'), resource_string(__name__, 'js/src/collapsible.coffee'), resource_string(__name__, 'js/src/html/display.coffee'), @@ -23,7 +28,6 @@ class AnnotatableModule(XModule): css = {'scss': [resource_string(__name__, 'css/annotatable/display.scss')]} icon_class = 'annotatable' - data = String(help="XML data for the annotation", scope=Scope.content) def __init__(self, *args, **kwargs): XModule.__init__(self, *args, **kwargs) @@ -125,7 +129,7 @@ class AnnotatableModule(XModule): return self.system.render_template('annotatable.html', context) -class AnnotatableDescriptor(RawDescriptor): +class AnnotatableDescriptor(AnnotatableFields, RawDescriptor): module_class = AnnotatableModule stores_state = True template_dir_name = "annotatable" diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 5f5ae05c7a..9c38347900 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -83,13 +83,7 @@ class ComplexEncoder(json.JSONEncoder): return json.JSONEncoder.default(self, obj) -class CapaModule(XModule): - ''' - An XModule implementing LonCapa format problems, implemented by way of - capa.capa_problem.LoncapaProblem - ''' - icon_class = 'problem' - +class CapaFields(object): attempts = StringyInteger(help="Number of attempts taken by the student on this problem", default=0, scope=Scope.student_state) max_attempts = StringyInteger(help="Maximum number of attempts that a student is allowed", scope=Scope.settings) due = String(help="Date that this problem is due by", scope=Scope.settings) @@ -103,6 +97,17 @@ class CapaModule(XModule): done = Boolean(help="Whether the student has answered the problem", scope=Scope.student_state) display_name = String(help="Display name for this module", scope=Scope.settings) seed = StringyInteger(help="Random seed for this student", scope=Scope.student_state) + weight = StringyFloat(help="How much to weight this problem by", scope=Scope.settings) + markdown = String(help="Markdown source of this module", scope=Scope.settings) + + +class CapaModule(CapaFields, XModule): + ''' + An XModule implementing LonCapa format problems, implemented by way of + capa.capa_problem.LoncapaProblem + ''' + icon_class = 'problem' + js = {'coffee': [resource_string(__name__, 'js/src/capa/display.coffee'), resource_string(__name__, 'js/src/collapsible.coffee'), @@ -792,7 +797,7 @@ class CapaModule(XModule): 'html': self.get_problem_html(encapsulate=False)} -class CapaDescriptor(RawDescriptor): +class CapaDescriptor(CapaFields, RawDescriptor): """ Module implementing problems in the LON-CAPA format, as implemented by capa.capa_problem @@ -800,9 +805,6 @@ class CapaDescriptor(RawDescriptor): module_class = CapaModule - weight = StringyFloat(help="How much to weight this problem by", scope=Scope.settings) - markdown = String(help="Markdown source of this module", scope=Scope.settings) - stores_state = True has_score = True template_dir_name = 'problem' diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index 33483037db..fd527f88d7 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -27,7 +27,26 @@ VERSION_TUPLES = ( DEFAULT_VERSION = 1 DEFAULT_VERSION = str(DEFAULT_VERSION) -class CombinedOpenEndedModule(XModule): + +class CombinedOpenEndedFields(object): + display_name = String(help="Display name for this module", default="Open Ended Grading", scope=Scope.settings) + current_task_number = Integer(help="Current task that the student is on.", default=0, scope=Scope.student_state) + task_states = List(help="List of state dictionaries of each task within this module.", scope=Scope.student_state) + state = String(help="Which step within the current task that the student is on.", default="initial", scope=Scope.student_state) + student_attempts = Integer(help="Number of attempts taken by the student on this problem", default=0, scope=Scope.student_state) + ready_to_reset = Boolean(help="If the problem is ready to be reset or not.", default=False, scope=Scope.student_state) + attempts = Integer(help="Maximum number of attempts that a student is allowed.", default=1, scope=Scope.settings) + is_graded = Boolean(help="Whether or not the problem is graded.", default=False, scope=Scope.settings) + accept_file_upload = Boolean(help="Whether or not the problem accepts file uploads.", default=False, scope=Scope.settings) + skip_spelling_checks = Boolean(help="Whether or not to skip initial spelling checks.", default=True, scope=Scope.settings) + due = String(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) + max_score = Integer(help="Maximum score for the problem.", default=1, scope=Scope.settings) + version = Integer(help="Current version number", default=DEFAULT_VERSION, scope=Scope.settings) + data = String(help="XML data for the problem", scope=Scope.content) + + +class CombinedOpenEndedModule(CombinedOpenEndedFields, XModule): """ This is a module that encapsulates all open ended grading (self assessment, peer assessment, etc). It transitions between problems, and support arbitrary ordering. @@ -60,22 +79,6 @@ class CombinedOpenEndedModule(XModule): icon_class = 'problem' - display_name = String(help="Display name for this module", default="Open Ended Grading", scope=Scope.settings) - current_task_number = Integer(help="Current task that the student is on.", default=0, scope=Scope.student_state) - task_states = List(help="List of state dictionaries of each task within this module.", scope=Scope.student_state) - state = String(help="Which step within the current task that the student is on.", default="initial", scope=Scope.student_state) - student_attempts = Integer(help="Number of attempts taken by the student on this problem", default=0, scope=Scope.student_state) - ready_to_reset = Boolean(help="If the problem is ready to be reset or not.", default=False, scope=Scope.student_state) - attempts = Integer(help="Maximum number of attempts that a student is allowed.", default=1, scope=Scope.settings) - is_graded = Boolean(help="Whether or not the problem is graded.", default=False, scope=Scope.settings) - accept_file_upload = Boolean(help="Whether or not the problem accepts file uploads.", default=False, scope=Scope.settings) - skip_spelling_checks = Boolean(help="Whether or not to skip initial spelling checks.", default=True, scope=Scope.settings) - due = String(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) - max_score = Integer(help="Maximum score for the problem.", default=1, scope=Scope.settings) - version = Integer(help="Current version number", default=DEFAULT_VERSION, scope=Scope.settings) - data = String(help="XML data for the problem", scope=Scope.content) - js = {'coffee': [resource_string(__name__, 'js/src/combinedopenended/display.coffee'), resource_string(__name__, 'js/src/collapsible.coffee'), resource_string(__name__, 'js/src/javascript_loader.coffee'), @@ -192,7 +195,7 @@ class CombinedOpenEndedModule(XModule): setattr(self,attribute, getattr(self.child_module,attribute)) -class CombinedOpenEndedDescriptor(RawDescriptor): +class CombinedOpenEndedDescriptor(CombinedOpenEndedFields, RawDescriptor): """ Module for adding combined open ended questions """ diff --git a/common/lib/xmodule/xmodule/conditional_module.py b/common/lib/xmodule/xmodule/conditional_module.py index 8e1290400f..a9375cae78 100644 --- a/common/lib/xmodule/xmodule/conditional_module.py +++ b/common/lib/xmodule/xmodule/conditional_module.py @@ -17,7 +17,11 @@ from xmodule.modulestore.exceptions import ItemNotFoundError log = logging.getLogger('mitx.' + __name__) -class ConditionalModule(XModule): +class ConditionalFields(object): + show_tag_list = List(help="Poll answers", scope=Scope.content) + + +class ConditionalModule(ConditionalFields, XModule): """ Blocks child module from showing unless certain conditions are met. @@ -134,7 +138,7 @@ class ConditionalModule(XModule): return new_class -class ConditionalDescriptor(SequenceDescriptor): +class ConditionalDescriptor(ConditionalFields, SequenceDescriptor): """Descriptor for conditional xmodule.""" _tag_name = 'conditional' @@ -145,7 +149,6 @@ class ConditionalDescriptor(SequenceDescriptor): stores_state = True has_score = False - show_tag_list = List(help="Poll answers", scope=Scope.content) @staticmethod def parse_sources(xml_element, system, return_descriptor=False): diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index 9c757d0ac6..7c47e0887a 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -147,9 +147,7 @@ class TextbookList(List): return json_data -class CourseDescriptor(SequenceDescriptor): - module_class = SequenceModule - +class CourseFields(object): textbooks = TextbookList(help="List of pairs of (title, url) for textbooks used in this course", scope=Scope.content) wiki_slug = String(help="Slug that points to the wiki for this course", scope=Scope.content) enrollment_start = Date(help="Date that enrollment for this class is opened", scope=Scope.settings) @@ -207,6 +205,10 @@ class CourseDescriptor(SequenceDescriptor): # Explicit comparison to True because we always want to return a bool. hide_progress_tab = Boolean(help="DO NOT USE THIS", scope=Scope.settings) + +class CourseDescriptor(CourseFields, SequenceDescriptor): + module_class = SequenceModule + template_dir_name = 'course' diff --git a/common/lib/xmodule/xmodule/discussion_module.py b/common/lib/xmodule/xmodule/discussion_module.py index 86325a19e8..7725a88e77 100644 --- a/common/lib/xmodule/xmodule/discussion_module.py +++ b/common/lib/xmodule/xmodule/discussion_module.py @@ -6,17 +6,20 @@ from xmodule.raw_module import RawDescriptor from xblock.core import String, Scope -class DiscussionModule(XModule): +class DiscussionFields(object): + discussion_id = String(scope=Scope.settings) + discussion_category = String(scope=Scope.settings) + discussion_target = String(scope=Scope.settings) + sort_key = String(scope=Scope.settings) + + +class DiscussionModule(DiscussionFields, XModule): js = {'coffee': [resource_string(__name__, 'js/src/time.coffee'), resource_string(__name__, 'js/src/discussion/display.coffee')] } js_module_name = "InlineDiscussion" - discussion_id = String(scope=Scope.settings) - discussion_category = String(scope=Scope.settings) - discussion_target = String(scope=Scope.settings) - sort_key = String(scope=Scope.settings) def get_html(self): context = { @@ -25,7 +28,7 @@ class DiscussionModule(XModule): return self.system.render_template('discussion/_discussion_module.html', context) -class DiscussionDescriptor(RawDescriptor): +class DiscussionDescriptor(DiscussionFields, RawDescriptor): module_class = DiscussionModule template_dir_name = "discussion" @@ -35,8 +38,3 @@ class DiscussionDescriptor(RawDescriptor): metadata_translations = dict(RawDescriptor.metadata_translations) metadata_translations['id'] = 'discussion_id' metadata_translations['for'] = 'discussion_target' - - discussion_id = String(scope=Scope.settings) - discussion_category = String(scope=Scope.settings) - discussion_target = String(scope=Scope.settings) - sort_key = String(scope=Scope.settings) diff --git a/common/lib/xmodule/xmodule/editing_module.py b/common/lib/xmodule/xmodule/editing_module.py index 9ff0124dc6..b93727a96b 100644 --- a/common/lib/xmodule/xmodule/editing_module.py +++ b/common/lib/xmodule/xmodule/editing_module.py @@ -6,7 +6,11 @@ import logging log = logging.getLogger(__name__) -class EditingDescriptor(MakoModuleDescriptor): +class EditingFields(object): + data = String(scope=Scope.content, default='') + + +class EditingDescriptor(EditingFields, MakoModuleDescriptor): """ Module that provides a raw editing view of its data and children. It does not perform any validation on its definition---just passes it along to the browser. @@ -15,8 +19,6 @@ class EditingDescriptor(MakoModuleDescriptor): """ mako_template = "widgets/raw-edit.html" - data = String(scope=Scope.content, default='') - # cdodge: a little refactoring here, since we're basically doing the same thing # here as with our parent class, let's call into it to get the basic fields # set and then add our additional fields. Trying to keep it DRY. diff --git a/common/lib/xmodule/xmodule/error_module.py b/common/lib/xmodule/xmodule/error_module.py index 3ed53cae91..d2135302da 100644 --- a/common/lib/xmodule/xmodule/error_module.py +++ b/common/lib/xmodule/xmodule/error_module.py @@ -21,10 +21,13 @@ log = logging.getLogger(__name__) # decides whether to create a staff or not-staff module. -class ErrorModule(XModule): - +class ErrorFields(object): contents = String(scope=Scope.content) error_msg = String(scope=Scope.content) + display_name = String(scope=Scope.settings) + + +class ErrorModule(ErrorFields, XModule): def get_html(self): '''Show an error to staff. @@ -38,7 +41,7 @@ class ErrorModule(XModule): }) -class NonStaffErrorModule(XModule): +class NonStaffErrorModule(ErrorFields, XModule): def get_html(self): '''Show an error to a student. TODO (vshnayder): proper style, divs, etc. @@ -51,16 +54,12 @@ class NonStaffErrorModule(XModule): }) -class ErrorDescriptor(JSONEditingDescriptor): +class ErrorDescriptor(ErrorFields, JSONEditingDescriptor): """ Module that provides a raw editing view of broken xml. """ module_class = ErrorModule - contents = String(scope=Scope.content) - error_msg = String(scope=Scope.content) - display_name = String(scope=Scope.settings) - @classmethod def _construct(self, system, contents, error_msg, location): diff --git a/common/lib/xmodule/xmodule/foldit_module.py b/common/lib/xmodule/xmodule/foldit_module.py index 3bec3107ff..d8111eecac 100644 --- a/common/lib/xmodule/xmodule/foldit_module.py +++ b/common/lib/xmodule/xmodule/foldit_module.py @@ -11,10 +11,8 @@ from xblock.core import Scope, Integer, String log = logging.getLogger(__name__) -class FolditModule(XModule): - - css = {'scss': [resource_string(__name__, 'css/foldit/leaderboard.scss')]} +class FolditFields(object): # default to what Spring_7012x uses required_level = Integer(default=4, scope=Scope.settings) required_sublevel = Integer(default=5, scope=Scope.settings) @@ -23,6 +21,11 @@ class FolditModule(XModule): show_basic_score = String(scope=Scope.settings, default='false') show_leaderboard = String(scope=Scope.settings, default='false') + +class FolditModule(FolditFields, XModule): + + css = {'scss': [resource_string(__name__, 'css/foldit/leaderboard.scss')]} + def __init__(self, *args, **kwargs): XModule.__init__(self, *args, **kwargs) """ @@ -154,7 +157,7 @@ class FolditModule(XModule): -class FolditDescriptor(XmlDescriptor, EditingDescriptor): +class FolditDescriptor(FolditFields, XmlDescriptor, EditingDescriptor): """ Module for adding Foldit problems to courses """ diff --git a/common/lib/xmodule/xmodule/gst_module.py b/common/lib/xmodule/xmodule/gst_module.py index ebbab0ec5a..00e8cf1f10 100644 --- a/common/lib/xmodule/xmodule/gst_module.py +++ b/common/lib/xmodule/xmodule/gst_module.py @@ -20,7 +20,12 @@ from xblock.core import String, Scope log = logging.getLogger(__name__) -class GraphicalSliderToolModule(XModule): +class GraphicalSliderToolFields(object): + render = String(scope=Scope.content) + configuration = String(scope=Scope.content) + + +class GraphicalSliderToolModule(GraphicalSliderToolFields, XModule): ''' Graphical-Slider-Tool Module ''' @@ -44,9 +49,6 @@ class GraphicalSliderToolModule(XModule): } js_module_name = "GraphicalSliderTool" - render = String(scope=Scope.content) - configuration = String(scope=Scope.content) - def get_html(self): """ Renders parameters to template. """ @@ -137,13 +139,10 @@ class GraphicalSliderToolModule(XModule): '">' + self.configuration + '')) -class GraphicalSliderToolDescriptor(MakoModuleDescriptor, XmlDescriptor): +class GraphicalSliderToolDescriptor(GraphicalSliderToolFields, MakoModuleDescriptor, XmlDescriptor): module_class = GraphicalSliderToolModule template_dir_name = 'graphical_slider_tool' - render = String(scope=Scope.content) - configuration = String(scope=Scope.content) - @classmethod def definition_from_xml(cls, xml_object, system): """ diff --git a/common/lib/xmodule/xmodule/html_module.py b/common/lib/xmodule/xmodule/html_module.py index 1f9228b47f..e9cec32e3e 100644 --- a/common/lib/xmodule/xmodule/html_module.py +++ b/common/lib/xmodule/xmodule/html_module.py @@ -17,7 +17,11 @@ from xmodule.xml_module import XmlDescriptor, name_to_pathname log = logging.getLogger("mitx.courseware") -class HtmlModule(XModule): +class HtmlFields(object): + data = String(help="Html contents to display for this module", scope=Scope.content) + + +class HtmlModule(HtmlFields, XModule): js = {'coffee': [resource_string(__name__, 'js/src/javascript_loader.coffee'), resource_string(__name__, 'js/src/collapsible.coffee'), resource_string(__name__, 'js/src/html/display.coffee') @@ -26,13 +30,11 @@ class HtmlModule(XModule): js_module_name = "HTMLModule" css = {'scss': [resource_string(__name__, 'css/html/display.scss')]} - data = String(help="Html contents to display for this module", scope=Scope.content) - def get_html(self): return self.data -class HtmlDescriptor(XmlDescriptor, EditingDescriptor): +class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor): """ Module for putting raw html in a course """ @@ -41,8 +43,6 @@ class HtmlDescriptor(XmlDescriptor, EditingDescriptor): filename_extension = "xml" template_dir_name = "html" - data = String(help="Html contents to display for this module", scope=Scope.content) - js = {'coffee': [resource_string(__name__, 'js/src/html/edit.coffee')]} js_module_name = "HTMLEditingDescriptor" css = {'scss': [resource_string(__name__, 'css/editor/edit.scss'), resource_string(__name__, 'css/html/edit.scss')]} diff --git a/common/lib/xmodule/xmodule/modulestore/mongo.py b/common/lib/xmodule/xmodule/modulestore/mongo.py index 4377c34361..c1a069ab12 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo.py @@ -95,7 +95,7 @@ class MongoKeyValueStore(KeyValueStore): else: return key.field_name in self._data else: - raise InvalidScopeError(key.scope) + return False MongoUsage = namedtuple('MongoUsage', 'id, def_id') diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py index 688013063e..709cd98263 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py @@ -5,7 +5,6 @@ from lxml import etree from xmodule.capa_module import ComplexEncoder from xmodule.progress import Progress from xmodule.stringify import stringify_children -from xblock.core import List, Integer, String, Scope import openendedchild from combined_open_ended_rubric import CombinedOpenEndedRubric diff --git a/common/lib/xmodule/xmodule/peer_grading_module.py b/common/lib/xmodule/xmodule/peer_grading_module.py index 5444981e2b..347a121614 100644 --- a/common/lib/xmodule/xmodule/peer_grading_module.py +++ b/common/lib/xmodule/xmodule/peer_grading_module.py @@ -27,7 +27,17 @@ IS_GRADED = True EXTERNAL_GRADER_NO_CONTACT_ERROR = "Failed to contact external graders. Please notify course staff." -class PeerGradingModule(XModule): +class PeerGradingFields(object): + use_for_single_location = Boolean(help="Whether to use this for a single location or as a panel.", default=USE_FOR_SINGLE_LOCATION, scope=Scope.settings) + link_to_location = String(help="The location this problem is linked to.", default=LINK_TO_LOCATION, scope=Scope.settings) + is_graded = Boolean(help="Whether or not this module is scored.",default=IS_GRADED, scope=Scope.settings) + display_due_date_string = String(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 = Integer(help="The maximum grade that a student can receieve for this problem.", default=MAX_SCORE, scope=Scope.settings) + student_data_for_location = Object(help="Student data for a given peer grading problem.", default=json.dumps({}),scope=Scope.student_state) + + +class PeerGradingModule(PeerGradingFields, XModule): _VERSION = 1 js = {'coffee': [resource_string(__name__, 'js/src/peergrading/peer_grading.coffee'), @@ -39,14 +49,6 @@ class PeerGradingModule(XModule): css = {'scss': [resource_string(__name__, 'css/combinedopenended/display.scss')]} - use_for_single_location = Boolean(help="Whether to use this for a single location or as a panel.", default=USE_FOR_SINGLE_LOCATION, scope=Scope.settings) - link_to_location = String(help="The location this problem is linked to.", default=LINK_TO_LOCATION, scope=Scope.settings) - is_graded = Boolean(help="Whether or not this module is scored.",default=IS_GRADED, scope=Scope.settings) - display_due_date_string = String(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 = Integer(help="The maximum grade that a student can receieve for this problem.", default=MAX_SCORE, scope=Scope.settings) - student_data_for_location = Object(help="Student data for a given peer grading problem.", default=json.dumps({}),scope=Scope.student_state) - def __init__(self, system, location, descriptor, model_data): XModule.__init__(self, system, location, descriptor, model_data) @@ -556,7 +558,7 @@ class PeerGradingModule(XModule): return json.dumps(state) -class PeerGradingDescriptor(RawDescriptor): +class PeerGradingDescriptor(PeerGradingFields, RawDescriptor): """ Module for adding peer grading questions """ diff --git a/common/lib/xmodule/xmodule/poll_module.py b/common/lib/xmodule/xmodule/poll_module.py index 321929030c..d5dabecdca 100644 --- a/common/lib/xmodule/xmodule/poll_module.py +++ b/common/lib/xmodule/xmodule/poll_module.py @@ -26,17 +26,7 @@ from xblock.core import Scope, String, Object, Boolean, List log = logging.getLogger(__name__) -class PollModule(XModule): - """Poll Module""" - js = { - 'coffee': [resource_string(__name__, 'js/src/javascript_loader.coffee')], - 'js': [resource_string(__name__, 'js/src/poll/logme.js'), - resource_string(__name__, 'js/src/poll/poll.js'), - resource_string(__name__, 'js/src/poll/poll_main.js')] - } - css = {'scss': [resource_string(__name__, 'css/poll/display.scss')]} - js_module_name = "Poll" - +class PollFields(object): # Name of poll to use in links to this poll display_name = String(help="Display name for this module", scope=Scope.settings) @@ -47,6 +37,20 @@ class PollModule(XModule): answers = List(help="Poll answers from xml", scope=Scope.content, default=[]) question = String(help="Poll question", scope=Scope.content, default='') + id = String(help="ID attribute for this module", scope=Scope.settings) + + +class PollModule(PollFields, XModule): + """Poll Module""" + js = { + 'coffee': [resource_string(__name__, 'js/src/javascript_loader.coffee')], + 'js': [resource_string(__name__, 'js/src/poll/logme.js'), + resource_string(__name__, 'js/src/poll/poll.js'), + resource_string(__name__, 'js/src/poll/poll_main.js')] + } + css = {'scss': [resource_string(__name__, 'css/poll/display.scss')]} + js_module_name = "Poll" + def handle_ajax(self, dispatch, get): """Ajax handler. @@ -135,7 +139,7 @@ class PollModule(XModule): 'reset': str(self.descriptor.xml_attributes.get('reset', 'true')).lower()}) -class PollDescriptor(MakoModuleDescriptor, XmlDescriptor): +class PollDescriptor(PollFields, MakoModuleDescriptor, XmlDescriptor): _tag_name = 'poll_question' _child_tag_name = 'answer' @@ -143,11 +147,6 @@ class PollDescriptor(MakoModuleDescriptor, XmlDescriptor): template_dir_name = 'poll' stores_state = True - answers = List(help="Poll answers", scope=Scope.content, default=[]) - question = String(help="Poll question", scope=Scope.content, default='') - display_name = String(help="Display name for this module", scope=Scope.settings) - id = String(help="ID attribute for this module", scope=Scope.settings) - @classmethod def definition_from_xml(cls, xml_object, system): """Pull out the data into dictionary. diff --git a/common/lib/xmodule/xmodule/randomize_module.py b/common/lib/xmodule/xmodule/randomize_module.py index aa926b836d..6620ab3cf7 100644 --- a/common/lib/xmodule/xmodule/randomize_module.py +++ b/common/lib/xmodule/xmodule/randomize_module.py @@ -9,7 +9,11 @@ from xblock.core import Scope, Integer log = logging.getLogger('mitx.' + __name__) -class RandomizeModule(XModule): +class RandomizeFields(object): + choice = Integer(help="Which random child was chosen", scope=Scope.student_state) + + +class RandomizeModule(RandomizeFields, XModule): """ Chooses a random child module. Chooses the same one every time for each student. @@ -31,9 +35,6 @@ class RandomizeModule(XModule): grading interaction is a tangle between super and subclasses of descriptors and modules. """ - - choice = Integer(help="Which random child was chosen", scope=Scope.student_state) - def __init__(self, *args, **kwargs): XModule.__init__(self, *args, **kwargs) @@ -64,7 +65,6 @@ class RandomizeModule(XModule): self.child_descriptor = None self.child = None - def get_child_descriptors(self): """ For grading--return just the chosen child. @@ -86,7 +86,7 @@ class RandomizeModule(XModule): return self.child.get_icon_class() if self.child else 'other' -class RandomizeDescriptor(SequenceDescriptor): +class RandomizeDescriptor(RandomizeFields, SequenceDescriptor): # the editing interface can be the same as for sequences -- just a container module_class = RandomizeModule diff --git a/common/lib/xmodule/xmodule/seq_module.py b/common/lib/xmodule/xmodule/seq_module.py index c0f85f4cec..f8e982f1a0 100644 --- a/common/lib/xmodule/xmodule/seq_module.py +++ b/common/lib/xmodule/xmodule/seq_module.py @@ -18,7 +18,15 @@ log = logging.getLogger(__name__) class_priority = ['video', 'problem'] -class SequenceModule(XModule): +class SequenceFields(object): + has_children = True + + # NOTE: Position is 1-indexed. This is silly, but there are now student + # positions saved on prod, so it's not easy to fix. + position = Integer(help="Last tab viewed in this sequence", scope=Scope.student_state) + + +class SequenceModule(SequenceFields, XModule): ''' Layout module which lays out content in a temporal sequence ''' js = {'coffee': [resource_string(__name__, @@ -27,11 +35,6 @@ class SequenceModule(XModule): css = {'scss': [resource_string(__name__, 'css/sequence/display.scss')]} js_module_name = "Sequence" - has_children = True - - # NOTE: Position is 1-indexed. This is silly, but there are now student - # positions saved on prod, so it's not easy to fix. - position = Integer(help="Last tab viewed in this sequence", scope=Scope.student_state) def __init__(self, *args, **kwargs): XModule.__init__(self, *args, **kwargs) @@ -114,11 +117,10 @@ class SequenceModule(XModule): return new_class -class SequenceDescriptor(MakoModuleDescriptor, XmlDescriptor): +class SequenceDescriptor(SequenceFields, MakoModuleDescriptor, XmlDescriptor): mako_template = 'widgets/sequence-edit.html' module_class = SequenceModule - has_children = True stores_state = True # For remembering where in the sequence the student is js = {'coffee': [resource_string(__name__, 'js/src/sequence/edit.coffee')]} diff --git a/common/lib/xmodule/xmodule/timelimit_module.py b/common/lib/xmodule/xmodule/timelimit_module.py index d327342d34..efa47a5dca 100644 --- a/common/lib/xmodule/xmodule/timelimit_module.py +++ b/common/lib/xmodule/xmodule/timelimit_module.py @@ -15,11 +15,7 @@ from xblock.core import Float, String, Boolean, Scope log = logging.getLogger(__name__) -class TimeLimitModule(XModule): - ''' - Wrapper module which imposes a time constraint for the completion of its child. - ''' - +class TimeLimitFields(object): beginning_at = Float(help="The time this timer was started", scope=Scope.student_state) ending_at = Float(help="The time this timer will end", scope=Scope.student_state) accomodation_code = String(help="A code indicating accommodations to be given the student", scope=Scope.student_state) @@ -27,6 +23,12 @@ class TimeLimitModule(XModule): duration = Float(help="The length of this timer", scope=Scope.settings) suppress_toplevel_navigation = Boolean(help="Whether the toplevel navigation should be suppressed when viewing this module", scope=Scope.settings) + +class TimeLimitModule(TimeLimitFields, XModule): + ''' + Wrapper module which imposes a time constraint for the completion of its child. + ''' + def __init__(self, *args, **kwargs): XModule.__init__(self, *args, **kwargs) @@ -117,7 +119,7 @@ class TimeLimitModule(XModule): else: return "other" -class TimeLimitDescriptor(XMLEditingDescriptor, XmlDescriptor): +class TimeLimitDescriptor(TimeLimitFields, XMLEditingDescriptor, XmlDescriptor): module_class = TimeLimitModule diff --git a/common/lib/xmodule/xmodule/video_module.py b/common/lib/xmodule/xmodule/video_module.py index 1396885bfb..4fc87b0750 100644 --- a/common/lib/xmodule/xmodule/video_module.py +++ b/common/lib/xmodule/xmodule/video_module.py @@ -19,7 +19,13 @@ import time log = logging.getLogger(__name__) -class VideoModule(XModule): +class VideoFields(object): + data = String(help="XML data for the problem", scope=Scope.content) + position = Integer(help="Current position in the video", scope=Scope.student_state, default=0) + display_name = String(help="Display name for this module", scope=Scope.settings) + + +class VideoModule(VideoFields, XModule): video_time = 0 icon_class = 'video' @@ -33,10 +39,6 @@ class VideoModule(XModule): css = {'scss': [resource_string(__name__, 'css/video/display.scss')]} js_module_name = "Video" - data = String(help="XML data for the problem", scope=Scope.content) - position = Integer(help="Current position in the video", scope=Scope.student_state, default=0) - display_name = String(help="Display name for this module", scope=Scope.settings) - def __init__(self, *args, **kwargs): XModule.__init__(self, *args, **kwargs) @@ -151,7 +153,7 @@ class VideoModule(XModule): }) -class VideoDescriptor(RawDescriptor): +class VideoDescriptor(VideoFields, RawDescriptor): module_class = VideoModule stores_state = True template_dir_name = "video" diff --git a/common/lib/xmodule/xmodule/videoalpha_module.py b/common/lib/xmodule/xmodule/videoalpha_module.py index c42c06c6be..5ae8d890e6 100644 --- a/common/lib/xmodule/xmodule/videoalpha_module.py +++ b/common/lib/xmodule/xmodule/videoalpha_module.py @@ -19,7 +19,13 @@ import time log = logging.getLogger(__name__) -class VideoAlphaModule(XModule): +class VideoAlphaFields(object): + data = String(help="XML data for the problem", scope=Scope.content) + position = Integer(help="Current position in the video", scope=Scope.student_state, default=0) + display_name = String(help="Display name for this module", scope=Scope.settings) + + +class VideoAlphaModule(VideoAlphaFields, XModule): """ XML source example: @@ -47,10 +53,6 @@ class VideoAlphaModule(XModule): css = {'scss': [resource_string(__name__, 'css/videoalpha/display.scss')]} js_module_name = "VideoAlpha" - data = String(help="XML data for the problem", scope=Scope.content) - position = Integer(help="Current position in the video", scope=Scope.student_state, default=0) - display_name = String(help="Display name for this module", scope=Scope.settings) - def __init__(self, *args, **kwargs): XModule.__init__(self, *args, **kwargs) xmltree = etree.fromstring(self.data) @@ -147,7 +149,7 @@ class VideoAlphaModule(XModule): }) -class VideoAlphaDescriptor(RawDescriptor): +class VideoAlphaDescriptor(VideoAlphaFields, RawDescriptor): module_class = VideoAlphaModule stores_state = True template_dir_name = "videoalpha" diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index e1327df49a..9b014e10cf 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -82,7 +82,15 @@ class HTMLSnippet(object): .format(self.__class__)) -class XModule(HTMLSnippet, XBlock): +class XModuleFields(object): + display_name = String( + help="Display name for this module", + scope=Scope.settings, + default=None, + ) + + +class XModule(XModuleFields, HTMLSnippet, XBlock): ''' Implements a generic learning module. Subclasses must at a minimum provide a definition for get_html in order @@ -99,11 +107,6 @@ class XModule(HTMLSnippet, XBlock): # in the module icon_class = 'other' - display_name = String( - help="Display name for this module", - scope=Scope.settings, - default=None, - ) def __init__(self, system, location, descriptor, model_data): ''' @@ -307,7 +310,7 @@ class ResourceTemplates(object): return templates -class XModuleDescriptor(HTMLSnippet, ResourceTemplates, XBlock): +class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock): """ An XModuleDescriptor is a specification for an element of a course. This could be a problem, an organizational element (a group of content), or a @@ -352,12 +355,6 @@ class XModuleDescriptor(HTMLSnippet, ResourceTemplates, XBlock): FoldIt, which posts grade-changing updates through a separate API. """ - display_name = String( - help="Display name for this module", - scope=Scope.settings, - default=None, - ) - # VS[compat]. Backwards compatibility code that can go away after # importing 2012 courses. # A set of metadata key conversions that we want to make diff --git a/local-requirements.txt b/local-requirements.txt index 512c6b3ff6..bea69ec53a 100644 --- a/local-requirements.txt +++ b/local-requirements.txt @@ -6,4 +6,4 @@ # XBlock: # Might change frequently, so put it in local-requirements.txt, # but conceptually is an external package, so it is in a separate repo. --e git+ssh://git@github.com/MITx/xmodule-debugger@6d5c2443#egg=XBlock +-e git+ssh://git@github.com/MITx/xmodule-debugger@5026e686#egg=XBlock