Document combined open ended module
This commit is contained in:
@@ -36,6 +36,10 @@ MAX_ATTEMPTS = 10000
|
||||
MAX_SCORE = 1
|
||||
|
||||
class CombinedOpenEndedModule(XModule):
|
||||
"""
|
||||
This is a module that encapsulates all open ended grading (self assessment, peer assessment, etc).
|
||||
It transitions between problems, and support arbitrary ordering.
|
||||
"""
|
||||
STATE_VERSION = 1
|
||||
|
||||
# states
|
||||
@@ -59,16 +63,37 @@ class CombinedOpenEndedModule(XModule):
|
||||
instance_state, shared_state, **kwargs)
|
||||
|
||||
"""
|
||||
Definition file should have multiple task blocks:
|
||||
Definition file should have one or many task blocks, a rubric block, and a prompt block:
|
||||
|
||||
Sample file:
|
||||
|
||||
<combinedopenended max_score="1" attempts="1">
|
||||
<task type="self">
|
||||
<combinedopenended attempts="10000" max_score="1">
|
||||
<rubric>
|
||||
Blah blah rubric.
|
||||
</rubric>
|
||||
<prompt>
|
||||
Some prompt.
|
||||
</prompt>
|
||||
<task>
|
||||
<selfassessment>
|
||||
<hintprompt>
|
||||
What hint about this problem would you give to someone?
|
||||
</hintprompt>
|
||||
<submitmessage>
|
||||
Save Succcesful. Thanks for participating!
|
||||
</submitmessage>
|
||||
</selfassessment>
|
||||
</task>
|
||||
<task>
|
||||
<openended min_score_to_attempt="1" max_score_to_attempt="1">
|
||||
<openendedparam>
|
||||
<initial_display>Enter essay here.</initial_display>
|
||||
<answer_display>This is the answer.</answer_display>
|
||||
<grader_payload>{"grader_settings" : "ml_grading.conf", "problem_id" : "6.002x/Welcome/OETest"}</grader_payload>
|
||||
</openendedparam>
|
||||
</openended>
|
||||
</task>
|
||||
</combinedopenended>
|
||||
|
||||
"""
|
||||
|
||||
# Load instance state
|
||||
@@ -77,17 +102,19 @@ class CombinedOpenEndedModule(XModule):
|
||||
else:
|
||||
instance_state = {}
|
||||
|
||||
# History is a list of tuples of (answer, score, hint), where hint may be
|
||||
# None for any element, and score and hint can be None for the last (current)
|
||||
# element.
|
||||
# Scores are on scale from 0 to max_score
|
||||
#We need to set the location here so the child modules can use it
|
||||
system.set('location', location)
|
||||
self.current_task_number = instance_state.get('current_task_number', 0)
|
||||
self.task_states= instance_state.get('task_states', [])
|
||||
|
||||
#Tells the system which xml definition to load
|
||||
self.current_task_number = instance_state.get('current_task_number', 0)
|
||||
#This loads the states of the individual children
|
||||
self.task_states= instance_state.get('task_states', [])
|
||||
#Overall state of the combined open ended module
|
||||
self.state = instance_state.get('state', 'initial')
|
||||
|
||||
self.attempts = instance_state.get('attempts', 0)
|
||||
|
||||
#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))
|
||||
|
||||
@@ -95,6 +122,7 @@ class CombinedOpenEndedModule(XModule):
|
||||
# completion (doesn't matter if you self-assessed correct/incorrect).
|
||||
self._max_score = int(self.metadata.get('max_score', MAX_SCORE))
|
||||
|
||||
#Static data is passed to the child modules to render
|
||||
self.static_data = {
|
||||
'max_score' : self._max_score,
|
||||
'max_attempts' : self.max_attempts,
|
||||
@@ -106,10 +134,21 @@ class CombinedOpenEndedModule(XModule):
|
||||
self.setup_next_task()
|
||||
|
||||
def get_tag_name(self, xml):
|
||||
"""
|
||||
Gets the tag name of a given xml block.
|
||||
Input: XML string
|
||||
Output: The name of the root tag
|
||||
"""
|
||||
tag=etree.fromstring(xml).tag
|
||||
return tag
|
||||
|
||||
def overwrite_state(self, current_task_state):
|
||||
"""
|
||||
Overwrites an instance state and sets the latest response to the current response. This is used
|
||||
to ensure that the student response is carried over from the first child to the rest.
|
||||
Input: Task state json string
|
||||
Output: Task state json string
|
||||
"""
|
||||
last_response_data=self.get_last_response(self.current_task_number-1)
|
||||
last_response = last_response_data['response']
|
||||
|
||||
@@ -122,6 +161,12 @@ class CombinedOpenEndedModule(XModule):
|
||||
return current_task_state
|
||||
|
||||
def child_modules(self):
|
||||
"""
|
||||
Returns the functions associated with the child modules in a dictionary. This makes writing functions
|
||||
simpler (saves code duplication)
|
||||
Input: None
|
||||
Output: A dictionary of dictionaries containing the descriptor functions and module functions
|
||||
"""
|
||||
child_modules={
|
||||
'openended' : open_ended_module.OpenEndedModule,
|
||||
'selfassessment' : self_assessment_module.SelfAssessmentModule,
|
||||
@@ -137,6 +182,12 @@ class CombinedOpenEndedModule(XModule):
|
||||
return children
|
||||
|
||||
def setup_next_task(self, reset=False):
|
||||
"""
|
||||
Sets up the next task for the module. Creates an instance state if none exists, carries over the answer
|
||||
from the last instance state to the next if needed.
|
||||
Input: A boolean indicating whether or not the reset function is calling.
|
||||
Output: Boolean True (not useful right now)
|
||||
"""
|
||||
current_task_state=None
|
||||
if len(self.task_states)>self.current_task_number:
|
||||
current_task_state=self.task_states[self.current_task_number]
|
||||
@@ -176,6 +227,12 @@ class CombinedOpenEndedModule(XModule):
|
||||
return True
|
||||
|
||||
def check_allow_reset(self):
|
||||
"""
|
||||
Checks to see if the student has passed the criteria to move to the next module. If not, sets
|
||||
allow_reset to true and halts the student progress through the tasks.
|
||||
Input: None
|
||||
Output: the allow_reset attribute of the current module.
|
||||
"""
|
||||
if not self.allow_reset:
|
||||
if self.current_task_number>0:
|
||||
last_response_data=self.get_last_response(self.current_task_number-1)
|
||||
@@ -188,6 +245,11 @@ class CombinedOpenEndedModule(XModule):
|
||||
return self.allow_reset
|
||||
|
||||
def get_context(self):
|
||||
"""
|
||||
Generates a context dictionary that is used to render html.
|
||||
Input: None
|
||||
Output: A dictionary that can be rendered into the combined open ended template.
|
||||
"""
|
||||
task_html=self.get_html_base()
|
||||
#set context variables and render template
|
||||
|
||||
@@ -200,27 +262,47 @@ class CombinedOpenEndedModule(XModule):
|
||||
'task_number' : self.current_task_number+1,
|
||||
'status' : self.get_status(),
|
||||
}
|
||||
log.debug(context)
|
||||
|
||||
return context
|
||||
|
||||
def get_html(self):
|
||||
"""
|
||||
Gets HTML for rendering.
|
||||
Input: None
|
||||
Output: rendered html
|
||||
"""
|
||||
context=self.get_context()
|
||||
html = self.system.render_template('combined_open_ended.html', context)
|
||||
return html
|
||||
|
||||
def get_html_nonsystem(self):
|
||||
"""
|
||||
Gets HTML for rendering via AJAX. Does not use system, because system contains some additional
|
||||
html, which is not appropriate for returning via ajax calls.
|
||||
Input: None
|
||||
Output: HTML rendered directly via Mako
|
||||
"""
|
||||
context=self.get_context()
|
||||
html = render_to_string('combined_open_ended.html', context)
|
||||
return html
|
||||
|
||||
def get_html_base(self):
|
||||
"""
|
||||
Gets the HTML associated with the current child task
|
||||
Input: None
|
||||
Output: Child task HTML
|
||||
"""
|
||||
self.update_task_states()
|
||||
html = self.current_task.get_html(self.system)
|
||||
return_html = rewrite_links(html, self.rewrite_content_links)
|
||||
return return_html
|
||||
|
||||
def get_current_attributes(self, task_number):
|
||||
"""
|
||||
Gets the min and max score to attempt attributes of the specified task.
|
||||
Input: The number of the task.
|
||||
Output: The minimum and maximum scores needed to move on to the specified task.
|
||||
"""
|
||||
task_xml=self.task_xml[task_number]
|
||||
etree_xml=etree.fromstring(task_xml)
|
||||
min_score_to_attempt=int(etree_xml.attrib.get('min_score_to_attempt',0))
|
||||
@@ -228,6 +310,11 @@ class CombinedOpenEndedModule(XModule):
|
||||
return {'min_score_to_attempt' : min_score_to_attempt, 'max_score_to_attempt' : max_score_to_attempt}
|
||||
|
||||
def get_last_response(self, task_number):
|
||||
"""
|
||||
Returns data associated with the specified task number, such as the last response, score, etc.
|
||||
Input: The number of the task.
|
||||
Output: A dictionary that contains information about the specified task.
|
||||
"""
|
||||
last_response=""
|
||||
task_state = self.task_states[task_number]
|
||||
task_xml=self.task_xml[task_number]
|
||||
@@ -270,6 +357,11 @@ class CombinedOpenEndedModule(XModule):
|
||||
return last_response_dict
|
||||
|
||||
def update_task_states(self):
|
||||
"""
|
||||
Updates the task state of the combined open ended module with the task state of the current child module.
|
||||
Input: None
|
||||
Output: boolean indicating whether or not the task state changed.
|
||||
"""
|
||||
changed=False
|
||||
if not self.allow_reset:
|
||||
self.task_states[self.current_task_number] = self.current_task.get_instance_state()
|
||||
@@ -286,6 +378,11 @@ class CombinedOpenEndedModule(XModule):
|
||||
return changed
|
||||
|
||||
def update_task_states_ajax(self,return_html):
|
||||
"""
|
||||
Runs the update task states function for ajax calls. Currently the same as update_task_states
|
||||
Input: The html returned by the handle_ajax function of the child
|
||||
Output: New html that should be rendered
|
||||
"""
|
||||
changed=self.update_task_states()
|
||||
if changed:
|
||||
#return_html=self.get_html()
|
||||
@@ -293,6 +390,11 @@ class CombinedOpenEndedModule(XModule):
|
||||
return return_html
|
||||
|
||||
def get_results(self, get):
|
||||
"""
|
||||
Gets the results of a given grader via ajax.
|
||||
Input: AJAX get dictionary
|
||||
Output: Dictionary to be rendered via ajax that contains the result html.
|
||||
"""
|
||||
task_number=int(get['task_number'])
|
||||
self.update_task_states()
|
||||
response_dict=self.get_last_response(task_number)
|
||||
@@ -325,15 +427,19 @@ class CombinedOpenEndedModule(XModule):
|
||||
return json.dumps(d,cls=ComplexEncoder)
|
||||
|
||||
def next_problem(self, get):
|
||||
"""
|
||||
Called via ajax to advance to the next problem.
|
||||
Input: AJAX get request.
|
||||
Output: Dictionary to be rendered
|
||||
"""
|
||||
self.update_task_states()
|
||||
return {'success' : True, 'html' : self.get_html_nonsystem(), 'allow_reset' : self.allow_reset}
|
||||
|
||||
def reset(self, get):
|
||||
"""
|
||||
If resetting is allowed, reset the state.
|
||||
|
||||
Returns {'success': bool, 'error': msg}
|
||||
(error only present if not success)
|
||||
If resetting is allowed, reset the state of the combined open ended module.
|
||||
Input: AJAX get dictionary
|
||||
Output: AJAX dictionary to tbe rendered
|
||||
"""
|
||||
if self.state != self.DONE:
|
||||
if not self.allow_reset:
|
||||
@@ -358,7 +464,9 @@ class CombinedOpenEndedModule(XModule):
|
||||
|
||||
def get_instance_state(self):
|
||||
"""
|
||||
Get the current score and state
|
||||
Returns the current instance state. The module can be recreated from the instance state.
|
||||
Input: None
|
||||
Output: A dictionary containing the instance state.
|
||||
"""
|
||||
|
||||
state = {
|
||||
@@ -373,6 +481,10 @@ class CombinedOpenEndedModule(XModule):
|
||||
return json.dumps(state)
|
||||
|
||||
def get_status(self):
|
||||
"""
|
||||
Input:
|
||||
Output:
|
||||
"""
|
||||
status=[]
|
||||
for i in xrange(0,self.current_task_number+1):
|
||||
task_data = self.get_last_response(i)
|
||||
|
||||
Reference in New Issue
Block a user