def run(self): """ process the multiplechoice directive and generate html for output. :param self: :return: An MChoiceNode. """ super(MChoice, self).run() TEMPLATE_START = ''' <div class="%(divclass)s"> <ul data-component="multiplechoice" data-multipleanswers="%(multipleAnswers)s" %(random)s id="%(divid)s"> ''' OPTION = ''' <li data-component="answer" %(is_correct)s id="%(divid)s_opt_%(alabel)s">%(atext)s</li><li data-component="feedback" id="%(divid)s_opt_%(alabel)s">%(feedtext)s</li> ''' TEMPLATE_END = ''' </ul> </div> ''' addQuestionToDB(self) mcNode = MChoiceNode(self.options, rawsource=self.block_text) mcNode.source, mcNode.line = self.state_machine.get_source_and_line(self.lineno) mcNode.template_start = TEMPLATE_START mcNode.template_option = OPTION mcNode.template_end = TEMPLATE_END self.state.nested_parse(self.content, self.content_offset, mcNode) env = self.state.document.settings.env self.options['divclass'] = env.config.mchoice_div_class # Expected _`structure`, with assigned variable names and transformations made: # # .. code-block:: # :number-lines: # # mcNode = MChoiceNode() # Item 1 of problem text # ... # Item n of problem text # answers_bullet_list = bullet_list() -> AnswersBulletList() <-- last item of mcNode may be a bulleted list of answers and feedback. # answer_list_item = list_item() -> AnswerListItem() # Item 1 of text for answer A # ... # Item n of text for answer A # feedback_bullet_list = bullet_list() -> FeedbackBulletList() <-- last item must be a bulleted list containing feedback. # feedback_list_item = list_item() -> FeedbackListItem() <-- A single item in the list, which contains the feedback. # answer_list_item = list_item() -> AnswerListItem() # Item 1 of text for answer B # ...and so on... # # See if the last item is a list. If so, and questions/answers weren't specified as options, assume it contains questions and answers. answers_bullet_list = mcNode[-1] if len(mcNode) else None if isinstance(answers_bullet_list, nodes.bullet_list) and ('answer_a' not in self.options and ('correct' not in self.options)): # Accumulate the correct answers. correct_answers = [] # Walk it, processing each answer and its associated feedback. for answer_list_item in answers_bullet_list: assert isinstance(answer_list_item, nodes.list_item) # Look for the feedback for this answer -- the last child of this answer list item. feedback_bullet_list = answer_list_item[-1] if ((not isinstance(feedback_bullet_list, nodes.bullet_list) or # It should have just one item (the feedback itself). (len(feedback_bullet_list) != 1))): raise self.error('On line {}, a single-item list must be nested under each answer.'.format(get_node_line(feedback_bullet_list))) # Check for a correct answer. feedback_list_item = feedback_bullet_list[0] assert isinstance(feedback_list_item, nodes.list_item) if feedback_bullet_list['bullet'] == '+': correct_answers.append(chr(answer_list_item.parent.index(answer_list_item) + ord('a'))) # Change the feedback list item (which is currently a generic list_item) to our special node class (a FeedbackListItem). feedback_list_item.replace_self(FeedbackListItem(feedback_list_item.rawsource, *feedback_list_item.children, **feedback_list_item.attributes)) # Change the feedback bulleted list (currently a bullet_list) to our class (a FeedbackBulletList). feedback_bullet_list.replace_self(FeedbackBulletList(feedback_bullet_list.rawsource, *feedback_bullet_list.children, **feedback_bullet_list.attributes)) # Change the answer list item (currently a list_item) to an AnswerListItem. answer_list_item.replace_self(AnswerListItem(answer_list_item.rawsource, *answer_list_item.children, **answer_list_item.attributes)) # Change the answer bulleted list (currently a bullet_list) to an AnswersBulletList. answers_bullet_list.replace_self(AnswersBulletList(answers_bullet_list.rawsource, *answers_bullet_list.children, **answers_bullet_list.attributes)) # Store the correct answers. self.options['correct'] = ','.join(correct_answers) # Check that a correct answer was provided. if not self.options.get('correct'): raise self.error('No correct answer specified.') return [mcNode]
def run(self): """ process the multiplechoice directive and generate html for output. :param self: :return: An MChoiceNode. """ super(MChoice, self).run() TEMPLATE_START = """ <div class="%(divclass)s"> <ul data-component="multiplechoice" data-question_label="%(question_label)s" data-multipleanswers="%(multipleAnswers)s" %(random)s id="%(divid)s" %(optional)s> """ OPTION = """ <li data-component="answer" %(is_correct)s id="%(divid)s_opt_%(alabel)s">%(atext)s</li><li data-component="feedback">%(feedtext)s</li> """ TEMPLATE_END = """ </ul> </div> """ addQuestionToDB(self) mcNode = MChoiceNode(self.options, rawsource=self.block_text) mcNode.source, mcNode.line = self.state_machine.get_source_and_line( self.lineno) mcNode.template_start = TEMPLATE_START mcNode.template_option = OPTION mcNode.template_end = TEMPLATE_END # For MChoice its better to insert the qnum into the content before further processing. self.updateContent() self.state.nested_parse(self.content, self.content_offset, mcNode) env = self.state.document.settings.env self.options["divclass"] = env.config.mchoice_div_class # Expected _`structure`, with assigned variable names and transformations made: # # .. code-block:: # :number-lines: # # mcNode = MChoiceNode() # Item 1 of problem text # ... # Item n of problem text # answers_bullet_list = bullet_list() -> AnswersBulletList() <-- last item of mcNode may be a bulleted list of answers and feedback. # answer_list_item = list_item() -> AnswerListItem() # Item 1 of text for answer A # ... # Item n of text for answer A # feedback_bullet_list = bullet_list() -> FeedbackBulletList() <-- last item must be a bulleted list containing feedback. # feedback_list_item = list_item() -> FeedbackListItem() <-- A single item in the list, which contains the feedback. # answer_list_item = list_item() -> AnswerListItem() # Item 1 of text for answer B # ...and so on... # # See if the last item is a list. If so, and questions/answers weren't specified as options, assume it contains questions and answers. answers_bullet_list = mcNode[-1] if len(mcNode) else None if isinstance(answers_bullet_list, nodes.bullet_list) and ("answer_a" not in self.options and ("correct" not in self.options)): # Accumulate the correct answers. correct_answers = [] # Walk it, processing each answer and its associated feedback. for answer_list_item in answers_bullet_list: assert isinstance(answer_list_item, nodes.list_item) # Look for the feedback for this answer -- the last child of this answer list item. feedback_bullet_list = answer_list_item[-1] if (not isinstance(feedback_bullet_list, nodes.bullet_list) or # It should have just one item (the feedback itself). (len(feedback_bullet_list) != 1)): raise self.error( "On line {}, a single-item list must be nested under each answer." .format(get_node_line(feedback_bullet_list))) # Check for a correct answer. feedback_list_item = feedback_bullet_list[0] assert isinstance(feedback_list_item, nodes.list_item) if feedback_bullet_list["bullet"] == "+": correct_answers.append( chr( answer_list_item.parent.index(answer_list_item) + ord("a"))) # Change the feedback list item (which is currently a generic list_item) to our special node class (a FeedbackListItem). feedback_list_item.replace_self( FeedbackListItem(feedback_list_item.rawsource, *feedback_list_item.children, **feedback_list_item.attributes)) # Change the feedback bulleted list (currently a bullet_list) to our class (a FeedbackBulletList). feedback_bullet_list.replace_self( FeedbackBulletList(feedback_bullet_list.rawsource, *feedback_bullet_list.children, **feedback_bullet_list.attributes)) # Change the answer list item (currently a list_item) to an AnswerListItem. answer_list_item.replace_self( AnswerListItem(answer_list_item.rawsource, *answer_list_item.children, **answer_list_item.attributes)) # Change the answer bulleted list (currently a bullet_list) to an AnswersBulletList. answers_bullet_list.replace_self( AnswersBulletList(answers_bullet_list.rawsource, *answers_bullet_list.children, **answers_bullet_list.attributes)) # Store the correct answers. self.options["correct"] = ",".join(correct_answers) maybeAddToAssignment(self) # Check that a correct answer was provided. if not self.options.get("correct"): raise self.error("No correct answer specified.") return [mcNode]
# See if the last item is a list. If so, and questions/answers weren't specified as options, assume it contains questions and answers. answers_bullet_list = mcNode[-1] if len(mcNode) else None if isinstance(answers_bullet_list, nodes.bullet_list) and ('answer_a' not in self.options and ('correct' not in self.options)): # Accumulate the correct answers. correct_answers = [] # Walk it, processing each answer and its associated feedback. for answer_list_item in answers_bullet_list: assert isinstance(answer_list_item, nodes.list_item) # Look for the feedback for this answer -- the last child of this answer list item. feedback_bullet_list = answer_list_item[-1] if ((not isinstance(feedback_bullet_list, nodes.bullet_list) or # It should have just one item (the feedback itself). (len(feedback_bullet_list) != 1))): raise self.error('On line {}, a single-item list must be nested under each answer.'.format(get_node_line(feedback_bullet_list))) # Check for a correct answer. feedback_list_item = feedback_bullet_list[0] assert isinstance(feedback_list_item, nodes.list_item) if feedback_bullet_list['bullet'] == '+': correct_answers.append(chr(answer_list_item.parent.index(answer_list_item) + ord('a'))) # Change the feedback list item (which is currently a generic list_item) to our special node class (a FeedbackListItem). feedback_list_item.replace_self(FeedbackListItem(feedback_list_item.rawsource, *feedback_list_item.children, **feedback_list_item.attributes)) # Change the feedback bulleted list (currently a bullet_list) to our class (a FeedbackBulletList). feedback_bullet_list.replace_self(FeedbackBulletList(feedback_bullet_list.rawsource, *feedback_bullet_list.children, **feedback_bullet_list.attributes)) # Change the answer list item (currently a list_item) to an AnswerListItem. answer_list_item.replace_self(AnswerListItem(answer_list_item.rawsource, *answer_list_item.children, **answer_list_item.attributes))