Commit 92e99d7a by Calen Pennington

Move field definitions for XModules into mixin classes, so that descriptors and…

Move field definitions for XModules into mixin classes, so that descriptors and modules always have the same set of fields
parent f01c7f94
......@@ -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):
"""
......
......@@ -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"
......
......@@ -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'
......
......@@ -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
"""
......
......@@ -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):
......
......@@ -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'
......
......@@ -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)
......@@ -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.
......
......@@ -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):
......
......@@ -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
"""
......
......@@ -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 + '</root>'))
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):
"""
......
......@@ -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')]}
......
......@@ -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')
......
......@@ -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
......
......@@ -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
"""
......
......@@ -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.
......
......@@ -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
......
......@@ -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')]}
......
......@@ -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
......
......@@ -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"
......@@ -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"
......@@ -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
......
......@@ -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
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