Merge remote-tracking branch 'origin/feature/vik/fix-oe-storage' into feature/alex/poll-merged
This commit is contained in:
@@ -4,5 +4,5 @@ setup(
|
||||
name="capa",
|
||||
version="0.1",
|
||||
packages=find_packages(exclude=["tests"]),
|
||||
install_requires=['distribute==0.6.34', 'pyparsing==1.5.6'],
|
||||
install_requires=['distribute==0.6.30', 'pyparsing==1.5.6'],
|
||||
)
|
||||
|
||||
@@ -6,13 +6,18 @@ from pkg_resources import resource_string
|
||||
|
||||
from xmodule.raw_module import RawDescriptor
|
||||
from .x_module import XModule
|
||||
from xblock.core import Integer, Scope, BlockScope, ModelType, String, Boolean, Object, Float
|
||||
from xmodule.open_ended_grading_classes.combined_open_ended_modulev1 import CombinedOpenEndedV1Module, CombinedOpenEndedV1Descriptor
|
||||
|
||||
log = logging.getLogger("mitx.courseware")
|
||||
|
||||
|
||||
V1_ATTRIBUTES = ["display_name", "current_task_number", "task_states", "state",
|
||||
"attempts", "ready_to_reset", "max_attempts", "is_graded", "accept_file_upload",
|
||||
"skip_spelling_checks", "due", "graceperiod", "max_score", "data"]
|
||||
|
||||
VERSION_TUPLES = (
|
||||
('1', CombinedOpenEndedV1Descriptor, CombinedOpenEndedV1Module),
|
||||
('1', CombinedOpenEndedV1Descriptor, CombinedOpenEndedV1Module, V1_ATTRIBUTES),
|
||||
)
|
||||
|
||||
DEFAULT_VERSION = 1
|
||||
@@ -49,6 +54,24 @@ class CombinedOpenEndedModule(XModule):
|
||||
INTERMEDIATE_DONE = 'intermediate_done'
|
||||
DONE = 'done'
|
||||
|
||||
icon_class = 'problem'
|
||||
|
||||
display_name = String(help="Display name for this module", scope=Scope.settings)
|
||||
current_task_number = Integer(help="Current task that the student is on.", default=0, scope=Scope.student_state)
|
||||
task_states = String(help="State dictionaries of each task within this module.", default=json.dumps("[]"), scope=Scope.student_state)
|
||||
state = String(help="Which step within the current task that the student is on.", default="initial", scope=Scope.student_state)
|
||||
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)
|
||||
max_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'),
|
||||
@@ -57,10 +80,8 @@ class CombinedOpenEndedModule(XModule):
|
||||
|
||||
css = {'scss': [resource_string(__name__, 'css/combinedopenended/display.scss')]}
|
||||
|
||||
def __init__(self, system, location, definition, descriptor,
|
||||
instance_state=None, shared_state=None, **kwargs):
|
||||
XModule.__init__(self, system, location, definition, descriptor,
|
||||
instance_state, shared_state, **kwargs)
|
||||
def __init__(self, system, location, descriptor, model_data):
|
||||
XModule.__init__(self, system, location, descriptor, model_data)
|
||||
|
||||
"""
|
||||
Definition file should have one or many task blocks, a rubric block, and a prompt block:
|
||||
@@ -100,25 +121,10 @@ class CombinedOpenEndedModule(XModule):
|
||||
self.system = system
|
||||
self.system.set('location', location)
|
||||
|
||||
# Load instance state
|
||||
if instance_state is not None:
|
||||
instance_state = json.loads(instance_state)
|
||||
else:
|
||||
instance_state = {}
|
||||
|
||||
self.version = self.metadata.get('version', DEFAULT_VERSION)
|
||||
version_error_string = "Version of combined open ended module {0} is not correct. Going with version {1}"
|
||||
if not isinstance(self.version, basestring):
|
||||
try:
|
||||
self.version = str(self.version)
|
||||
except:
|
||||
#This is a dev_facing_error
|
||||
log.info(version_error_string.format(self.version, DEFAULT_VERSION))
|
||||
self.version = DEFAULT_VERSION
|
||||
|
||||
versions = [i[0] for i in VERSION_TUPLES]
|
||||
descriptors = [i[1] for i in VERSION_TUPLES]
|
||||
modules = [i[2] for i in VERSION_TUPLES]
|
||||
attributes = [i[3] for i in VERSION_TUPLES]
|
||||
|
||||
try:
|
||||
version_index = versions.index(self.version)
|
||||
@@ -132,10 +138,11 @@ class CombinedOpenEndedModule(XModule):
|
||||
'rewrite_content_links' : self.rewrite_content_links,
|
||||
}
|
||||
|
||||
instance_state = { k: self.__dict__[k] for k in self.__dict__ if k in attributes[version_index]}
|
||||
self.child_descriptor = descriptors[version_index](self.system)
|
||||
self.child_definition = descriptors[version_index].definition_from_xml(etree.fromstring(definition['data']), self.system)
|
||||
self.child_definition = descriptors[version_index].definition_from_xml(etree.fromstring(self.data), self.system)
|
||||
self.child_module = modules[version_index](self.system, location, self.child_definition, self.child_descriptor,
|
||||
instance_state = json.dumps(instance_state), metadata = self.metadata, static_data= static_data)
|
||||
instance_state = instance_state, static_data= static_data)
|
||||
|
||||
def get_html(self):
|
||||
return self.child_module.get_html()
|
||||
|
||||
@@ -115,17 +115,10 @@ class CombinedOpenEndedV1Module():
|
||||
|
||||
"""
|
||||
|
||||
self.metadata = metadata
|
||||
self.display_name = metadata.get('display_name', "Open Ended")
|
||||
self.instance_state = instance_state
|
||||
self.display_name = instance_state.get('display_name', "Open Ended")
|
||||
self.rewrite_content_links = static_data.get('rewrite_content_links',"")
|
||||
|
||||
|
||||
# Load instance state
|
||||
if instance_state is not None:
|
||||
instance_state = json.loads(instance_state)
|
||||
else:
|
||||
instance_state = {}
|
||||
|
||||
#We need to set the location here so the child modules can use it
|
||||
system.set('location', location)
|
||||
self.system = system
|
||||
@@ -141,14 +134,14 @@ class CombinedOpenEndedV1Module():
|
||||
|
||||
#Allow reset is true if student has failed the criteria to move to the next child task
|
||||
self.allow_reset = instance_state.get('ready_to_reset', False)
|
||||
self.max_attempts = int(self.metadata.get('attempts', MAX_ATTEMPTS))
|
||||
self.is_scored = self.metadata.get('is_graded', IS_SCORED) in TRUE_DICT
|
||||
self.accept_file_upload = self.metadata.get('accept_file_upload', ACCEPT_FILE_UPLOAD) in TRUE_DICT
|
||||
self.skip_basic_checks = self.metadata.get('skip_spelling_checks', SKIP_BASIC_CHECKS)
|
||||
self.max_attempts = int(self.instance_state.get('attempts', MAX_ATTEMPTS))
|
||||
self.is_scored = self.instance_state.get('is_graded', IS_SCORED) in TRUE_DICT
|
||||
self.accept_file_upload = self.instance_state.get('accept_file_upload', ACCEPT_FILE_UPLOAD) in TRUE_DICT
|
||||
self.skip_basic_checks = self.instance_state.get('skip_spelling_checks', SKIP_BASIC_CHECKS)
|
||||
|
||||
display_due_date_string = self.metadata.get('due', None)
|
||||
display_due_date_string = self.instance_state.get('due', None)
|
||||
|
||||
grace_period_string = self.metadata.get('graceperiod', None)
|
||||
grace_period_string = self.instance_state.get('graceperiod', None)
|
||||
try:
|
||||
self.timeinfo = TimeInfo(display_due_date_string, grace_period_string)
|
||||
except:
|
||||
@@ -158,7 +151,7 @@ class CombinedOpenEndedV1Module():
|
||||
|
||||
# Used for progress / grading. Currently get credit just for
|
||||
# completion (doesn't matter if you self-assessed correct/incorrect).
|
||||
self._max_score = int(self.metadata.get('max_score', MAX_SCORE))
|
||||
self._max_score = int(self.instance_state.get('max_score', MAX_SCORE))
|
||||
|
||||
self.rubric_renderer = CombinedOpenEndedRubric(system, True)
|
||||
rubric_string = stringify_children(definition['rubric'])
|
||||
@@ -760,7 +753,7 @@ class CombinedOpenEndedV1Module():
|
||||
return progress_object
|
||||
|
||||
|
||||
class CombinedOpenEndedV1Descriptor(XmlDescriptor, EditingDescriptor):
|
||||
class CombinedOpenEndedV1Descriptor():
|
||||
"""
|
||||
Module for adding combined open ended questions
|
||||
"""
|
||||
@@ -772,6 +765,9 @@ class CombinedOpenEndedV1Descriptor(XmlDescriptor, EditingDescriptor):
|
||||
has_score = True
|
||||
template_dir_name = "combinedopenended"
|
||||
|
||||
def __init__(self, system):
|
||||
self.system =system
|
||||
|
||||
@classmethod
|
||||
def definition_from_xml(cls, xml_object, system):
|
||||
"""
|
||||
@@ -798,7 +794,7 @@ class CombinedOpenEndedV1Descriptor(XmlDescriptor, EditingDescriptor):
|
||||
"""Assumes that xml_object has child k"""
|
||||
return xml_object.xpath(k)[0]
|
||||
|
||||
return {'task_xml': parse_task('task'), 'prompt': parse('prompt'), 'rubric': parse('rubric')}, []
|
||||
return {'task_xml': parse_task('task'), 'prompt': parse('prompt'), 'rubric': parse('rubric')}
|
||||
|
||||
|
||||
def definition_to_xml(self, resource_fs):
|
||||
|
||||
@@ -696,7 +696,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
|
||||
return html
|
||||
|
||||
|
||||
class OpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
class OpenEndedDescriptor():
|
||||
"""
|
||||
Module for adding open ended response questions to courses
|
||||
"""
|
||||
@@ -708,6 +708,9 @@ class OpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
has_score = True
|
||||
template_dir_name = "openended"
|
||||
|
||||
def __init__(self, system):
|
||||
self.system =system
|
||||
|
||||
@classmethod
|
||||
def definition_from_xml(cls, xml_object, system):
|
||||
"""
|
||||
@@ -727,7 +730,7 @@ class OpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
"""Assumes that xml_object has child k"""
|
||||
return xml_object.xpath(k)[0]
|
||||
|
||||
return {'oeparam': parse('openendedparam')}, []
|
||||
return {'oeparam': parse('openendedparam')}
|
||||
|
||||
|
||||
def definition_to_xml(self, resource_fs):
|
||||
|
||||
@@ -299,7 +299,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
return [rubric_scores]
|
||||
|
||||
|
||||
class SelfAssessmentDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
class SelfAssessmentDescriptor():
|
||||
"""
|
||||
Module for adding self assessment questions to courses
|
||||
"""
|
||||
@@ -311,6 +311,9 @@ class SelfAssessmentDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
has_score = True
|
||||
template_dir_name = "selfassessment"
|
||||
|
||||
def __init__(self, system):
|
||||
self.system =system
|
||||
|
||||
@classmethod
|
||||
def definition_from_xml(cls, xml_object, system):
|
||||
"""
|
||||
|
||||
@@ -6,10 +6,9 @@ from lxml import etree
|
||||
from datetime import datetime
|
||||
from pkg_resources import resource_string
|
||||
from .capa_module import ComplexEncoder
|
||||
from .editing_module import EditingDescriptor
|
||||
from .stringify import stringify_children
|
||||
from .x_module import XModule
|
||||
from .xml_module import XmlDescriptor
|
||||
from xmodule.raw_module import RawDescriptor
|
||||
from xmodule.modulestore import Location
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from timeinfo import TimeInfo
|
||||
@@ -40,14 +39,16 @@ class PeerGradingModule(XModule):
|
||||
|
||||
css = {'scss': [resource_string(__name__, 'css/combinedopenended/display.scss')]}
|
||||
|
||||
student_data_for_location = Object(scope=Scope.student_state)
|
||||
max_grade = Integer(default=MAX_SCORE, scope=Scope.student_state)
|
||||
use_for_single_location = Boolean(default=USE_FOR_SINGLE_LOCATION, scope=Scope.settings)
|
||||
is_graded = Boolean(default=IS_GRADED, scope=Scope.settings)
|
||||
link_to_location = String(default=LINK_TO_LOCATION, scope=Scope.settings)
|
||||
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, *args, **kwargs):
|
||||
super(PeerGradingModule, self).__init__(*args, **kwargs)
|
||||
def __init__(self, system, location, descriptor, model_data):
|
||||
XModule.__init__(self, system, location, descriptor, model_data)
|
||||
|
||||
#We need to set the location here so the child modules can use it
|
||||
system.set('location', location)
|
||||
@@ -57,11 +58,6 @@ class PeerGradingModule(XModule):
|
||||
else:
|
||||
self.peer_gs = MockPeerGradingService()
|
||||
|
||||
|
||||
if isinstance(self.use_for_single_location, basestring):
|
||||
self.use_for_single_location = (self.use_for_single_location in TRUE_DICT)
|
||||
|
||||
self.link_to_location = self.metadata.get('link_to_location', USE_FOR_SINGLE_LOCATION)
|
||||
if self.use_for_single_location == True:
|
||||
try:
|
||||
self.linked_problem = modulestore().get_instance(self.system.course_id, self.link_to_location)
|
||||
@@ -73,21 +69,18 @@ class PeerGradingModule(XModule):
|
||||
if due_date:
|
||||
self.metadata['due'] = due_date
|
||||
|
||||
self.is_graded = self.metadata.get('is_graded', IS_GRADED)
|
||||
if isinstance(self.is_graded, basestring):
|
||||
self.is_graded = (self.is_graded in TRUE_DICT)
|
||||
|
||||
display_due_date_string = self.metadata.get('due', None)
|
||||
grace_period_string = self.metadata.get('graceperiod', None)
|
||||
|
||||
try:
|
||||
self.timeinfo = TimeInfo(display_due_date_string, grace_period_string)
|
||||
self.timeinfo = TimeInfo(self.display_due_date_string, self.grace_period_string)
|
||||
except:
|
||||
log.error("Error parsing due date information in location {0}".format(location))
|
||||
raise
|
||||
|
||||
self.display_due_date = self.timeinfo.display_due_date
|
||||
|
||||
try:
|
||||
self.student_data_for_location = json.loads(self.student_data_for_location)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.ajax_url = self.system.ajax_url
|
||||
if not self.ajax_url.endswith("/"):
|
||||
@@ -558,9 +551,9 @@ class PeerGradingModule(XModule):
|
||||
return json.dumps(state)
|
||||
|
||||
|
||||
class PeerGradingDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
class PeerGradingDescriptor(RawDescriptor):
|
||||
"""
|
||||
Module for adding combined open ended questions
|
||||
Module for adding peer grading questions
|
||||
"""
|
||||
mako_template = "widgets/raw-edit.html"
|
||||
module_class = PeerGradingModule
|
||||
@@ -569,41 +562,3 @@ class PeerGradingDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
stores_state = True
|
||||
has_score = True
|
||||
template_dir_name = "peer_grading"
|
||||
|
||||
js = {'coffee': [resource_string(__name__, 'js/src/html/edit.coffee')]}
|
||||
js_module_name = "HTMLEditingDescriptor"
|
||||
|
||||
@classmethod
|
||||
def definition_from_xml(cls, xml_object, system):
|
||||
"""
|
||||
Pull out the individual tasks, the rubric, and the prompt, and parse
|
||||
|
||||
Returns:
|
||||
{
|
||||
'rubric': 'some-html',
|
||||
'prompt': 'some-html',
|
||||
'task_xml': dictionary of xml strings,
|
||||
}
|
||||
"""
|
||||
log.debug("In definition")
|
||||
expected_children = []
|
||||
for child in expected_children:
|
||||
if len(xml_object.xpath(child)) == 0:
|
||||
#This is a staff_facing_error
|
||||
raise ValueError("Peer grading definition must include at least one '{0}' tag. Contact the learning sciences group for assistance.".format(child))
|
||||
|
||||
def parse_task(k):
|
||||
"""Assumes that xml_object has child k"""
|
||||
return [stringify_children(xml_object.xpath(k)[i]) for i in xrange(0, len(xml_object.xpath(k)))]
|
||||
|
||||
def parse(k):
|
||||
"""Assumes that xml_object has child k"""
|
||||
return xml_object.xpath(k)[0]
|
||||
|
||||
return {}, []
|
||||
|
||||
|
||||
def definition_to_xml(self, resource_fs):
|
||||
'''Return an xml element representing this definition.'''
|
||||
elt = etree.Element('peergrading')
|
||||
return elt
|
||||
|
||||
@@ -59,7 +59,14 @@ def staff_grading_notifications(course, user):
|
||||
|
||||
|
||||
def peer_grading_notifications(course, user):
|
||||
system = ModuleSystem(None, None, None, render_to_string, None)
|
||||
system = ModuleSystem(
|
||||
ajax_url=None,
|
||||
track_function=None,
|
||||
get_module = None,
|
||||
render_template=render_to_string,
|
||||
replace_urls=None,
|
||||
xblock_model_data= {}
|
||||
)
|
||||
peer_gs = peer_grading_service.PeerGradingService(settings.OPEN_ENDED_GRADING_INTERFACE, system)
|
||||
pending_grading = False
|
||||
img_path = ""
|
||||
@@ -93,7 +100,14 @@ def peer_grading_notifications(course, user):
|
||||
|
||||
|
||||
def combined_notifications(course, user):
|
||||
system = ModuleSystem(None, None, None, render_to_string, None)
|
||||
system = ModuleSystem(
|
||||
ajax_url=None,
|
||||
track_function=None,
|
||||
get_module = None,
|
||||
render_template=render_to_string,
|
||||
replace_urls=None,
|
||||
xblock_model_data= {}
|
||||
)
|
||||
controller_qs = ControllerQueryService(settings.OPEN_ENDED_GRADING_INTERFACE, system)
|
||||
student_id = unique_id_for_user(user)
|
||||
user_is_staff = has_access(user, course, 'staff')
|
||||
|
||||
@@ -60,7 +60,14 @@ class StaffGradingService(GradingService):
|
||||
Interface to staff grading backend.
|
||||
"""
|
||||
def __init__(self, config):
|
||||
config['system'] = ModuleSystem(None, None, None, render_to_string, None)
|
||||
config['system'] = ModuleSystem(
|
||||
ajax_url=None,
|
||||
track_function=None,
|
||||
get_module = None,
|
||||
render_template=render_to_string,
|
||||
replace_urls=None,
|
||||
xblock_model_data= {}
|
||||
)
|
||||
super(StaffGradingService, self).__init__(config)
|
||||
self.url = config['url'] + config['staff_grading']
|
||||
self.login_url = self.url + '/login/'
|
||||
|
||||
@@ -145,14 +145,21 @@ class TestPeerGradingService(ct.PageLoader):
|
||||
self.course_id = "edX/toy/2012_Fall"
|
||||
self.toy = modulestore().get_course(self.course_id)
|
||||
location = "i4x://edX/toy/peergrading/init"
|
||||
|
||||
model_data = {'data' : "<peergrading/>"}
|
||||
self.mock_service = peer_grading_service.MockPeerGradingService()
|
||||
self.system = ModuleSystem(location, None, None, render_to_string, None,
|
||||
self.system = ModuleSystem(
|
||||
ajax_url=location,
|
||||
track_function=None,
|
||||
get_module = None,
|
||||
render_template=render_to_string,
|
||||
replace_urls=None,
|
||||
xblock_model_data= {},
|
||||
s3_interface = test_util_open_ended.S3_INTERFACE,
|
||||
open_ended_grading_interface=test_util_open_ended.OPEN_ENDED_GRADING_INTERFACE
|
||||
)
|
||||
self.descriptor = peer_grading_module.PeerGradingDescriptor(self.system)
|
||||
self.peer_module = peer_grading_module.PeerGradingModule(self.system, location, "<peergrading/>", self.descriptor)
|
||||
self.descriptor = peer_grading_module.PeerGradingDescriptor(self.system, location, model_data)
|
||||
model_data = {}
|
||||
self.peer_module = peer_grading_module.PeerGradingModule(self.system, location, self.descriptor, model_data)
|
||||
self.peer_module.peer_gs = self.mock_service
|
||||
self.logout()
|
||||
|
||||
|
||||
@@ -29,7 +29,14 @@ log = logging.getLogger(__name__)
|
||||
|
||||
template_imports = {'urllib': urllib}
|
||||
|
||||
system = ModuleSystem(None, None, None, render_to_string, None, None)
|
||||
system = ModuleSystem(
|
||||
ajax_url=None,
|
||||
track_function=None,
|
||||
get_module = None,
|
||||
render_template=render_to_string,
|
||||
replace_urls = None,
|
||||
xblock_model_data= {}
|
||||
)
|
||||
controller_qs = ControllerQueryService(settings.OPEN_ENDED_GRADING_INTERFACE, system)
|
||||
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user