Move field definitions for XModules into mixin classes, so that descriptors and modules always have the same set of fields

This commit is contained in:
Calen Pennington
2013-03-13 11:35:32 -04:00
parent f01c7f9469
commit 92e99d7a8b
23 changed files with 181 additions and 164 deletions

View File

@@ -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):
"""

View File

@@ -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"

View File

@@ -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'

View File

@@ -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
"""

View File

@@ -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):

View File

@@ -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'

View File

@@ -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)

View File

@@ -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.

View File

@@ -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):

View File

@@ -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
"""

View File

@@ -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):
"""

View File

@@ -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')]}

View File

@@ -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')

View File

@@ -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

View File

@@ -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
"""

View File

@@ -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.

View File

@@ -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

View File

@@ -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')]}

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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