From 2f841c8a334d329ea5d281668d714abd21c4d8d1 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Tue, 8 Jan 2013 18:55:28 -0500 Subject: [PATCH] Document combined open ended module --- .../xmodule/combined_open_ended_module.py | 144 ++++++++++++++++-- 1 file changed, 128 insertions(+), 16 deletions(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index ee36690b1c..a639d6997a 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -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: - - - + + + Blah blah rubric. + + + Some prompt. + + + + What hint about this problem would you give to someone? + + + Save Succcesful. Thanks for participating! + + + + + Enter essay here. + This is the answer. + {"grader_settings" : "ml_grading.conf", "problem_id" : "6.002x/Welcome/OETest"} + + + + """ # 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)