Commit 2f841c8a by Vik Paruchuri

Document combined open ended module

parent 38a81b46
......@@ -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)
#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)
......
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