def run(self): super(HParsonsDirective, self).run() addQuestionToDB(self) env = self.state.document.settings.env if "language" in self.options: self.options["language"] = "data-language='{}'".format(self.options["language"]) else: self.options["language"] = "" if "reuse" in self.options: self.options['reuse'] = ' data-reuse="true"' else: self.options['reuse'] = '' if "randomize" in self.options: self.options['randomize'] = ' data-randomize="true"' else: self.options['randomize'] = '' if "blockanswer" in self.options: self.options["blockanswer"] = "data-blockanswer='{}'".format(self.options["blockanswer"]) else: self.options['blockanswer'] = '' explain_text = None if self.content: if "~~~~" in self.content: idx = self.content.index("~~~~") explain_text = self.content[:idx] self.content = self.content[idx + 1 :] source = "\n".join(self.content) else: source = "\n" self.explain_text = explain_text or ["Not an Exercise"] self.options["initialsetting"] = source # SQL Options if "dburl" in self.options: self.options["dburl"] = "data-dburl='{}'".format(self.options["dburl"]) else: self.options["dburl"] = "" course_name = env.config.html_context["course_id"] divid = self.options["divid"] hpnode = HParsonsNode() hpnode["runestone_options"] = self.options hpnode["source"], hpnode["line"] = self.state_machine.get_source_and_line(self.lineno) self.add_name(hpnode) # make this divid available as a target for :ref: maybeAddToAssignment(self) if explain_text: self.updateContent() self.state.nested_parse(explain_text, self.content_offset, hpnode) return [hpnode]
def run(self): super(JournalDirective, self).run() addQuestionToDB(self) # Raise an error if the directive does not have contents. self.assert_has_content() self.options[ "mathjax"] = "data-mathjax" if "mathjax" in self.options else "" journal_node = JournalNode(self.options, rawsource=self.block_text) journal_node.source, journal_node.line = self.state_machine.get_source_and_line( self.lineno) self.updateContent() self.state.nested_parse(self.content, self.content_offset, journal_node) env = self.state.document.settings.env if self.options["optional"]: self.options[ "divclass"] = env.config.shortanswer_optional_div_class else: self.options["divclass"] = env.config.shortanswer_div_class maybeAddToAssignment(self) return [journal_node]
def run(self): """ process the clickablearea directive and generate html for output. :param self: :return: .. clickablearea:: identifier :question: Question text :feedback: Optional feedback for incorrect answer :iscode: Boolean that if present will put the content into a <pre> :table: Boolean that indicates that the content is a table. :correct: An array of the indices of the correct elements, separated by semicolons--if this is a table, each item in the array is a tuple with the first number being the row and the second number being the column--use a column number of 0 to make the whole row correct (ex: 1,2;3,0 makes the 2nd data cell in the first row correct as well as the entire 3rd row) :incorrect: An array of the indices of the incorrect elements--same format as the correct elements. --Content-- """ super(ClickableArea, self).run() self.explain_text = [self.options["question"]] addQuestionToDB(self) self.assert_has_content() if "iscode" in self.options: source = "\n".join(self.content) source = source.replace(":click-correct:", "<span data-correct>") source = source.replace(":click-incorrect:", "<span data-incorrect>") source = source.replace(":endclick:", "</span>") source = "<pre>" + source + "</pre>" self.options["clickcode"] = source self.options["raw_source"] = self.content else: self.options["clickcode"] = "" clickNode = ClickableAreaNode() clickNode["runestone_options"] = self.options clickNode["source"], clickNode[ "line"] = self.state_machine.get_source_and_line(self.lineno) clickNode["template_start"] = TEMPLATE if "table" in self.options: self.options["table"] = "data-table" else: self.options["table"] = "" if "iscode" not in self.options: self.state.nested_parse(self.content, self.content_offset, clickNode) env = self.state.document.settings.env self.options["divclass"] = env.config.clickable_div_class maybeAddToAssignment(self) return [clickNode]
def run(self): """ process the multiplechoice directive and generate html for output. :param self: :return: .. dragndrop:: identifier :feedback: Feedback that is displayed if things are incorrectly matched--is optional :match_1: Draggable element text|||Dropzone to be matched with text :match_2: Drag to Answer B|||Answer B :match_3: Draggable text|||Text of dropzone ...etc...(up to 20 matches) The question goes here. """ super(DragNDrop, self).run() addQuestionToDB(self) if self.content: source = "\n".join(self.content) else: source = "\n" self.options["question"] = source env = self.state.document.settings.env self.options["divclass"] = env.config.dragndrop_div_class dndNode = DragNDropNode() dndNode["runestone_options"] = self.options dndNode["source"], dndNode["line"] = self.state_machine.get_source_and_line( self.lineno ) dndNode["template_start"] = TEMPLATE_START dndNode["template_option"] = TEMPLATE_OPTION dndNode["template_end"] = TEMPLATE_END maybeAddToAssignment(self) return [dndNode]
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]
def run(self): super(SelectQuestion, self).run() addQuestionToDB(self) env = self.state.document.settings.env is_dynamic = env.config.html_context.get("dynamic_pages", False) if is_dynamic: self.options["message"] = "Loading ..." else: self.options[ "message"] = "The selectquestion directive only works with dynamic pages" if "fromid" in self.options: self.question_bank_choices = self.options["fromid"] self.options[ "selector"] = f"data-questionlist='{self.question_bank_choices}'" else: self.options["selector"] = "" # todo: validate that question(s) are in the database self.options["component_id"] = self.arguments[0].strip() if "proficiency" in self.options: self.options[ "proficiency"] = f"""data-proficiency='{self.options["proficiency"]}'""" else: self.options["proficiency"] = "" if "points" in self.options: self.int_points = int(self.options["points"]) self.options["points"] = f"data-points={self.options['points']}" else: self.int_points = 1 self.options["points"] = "" if "min_difficulty" in self.options: self.options[ "min_difficulty"] = f"data-minDifficulty={self.options['min_difficulty']}" else: self.options["min_difficulty"] = "" if "max_difficulty" in self.options: self.options[ "max_difficulty"] = f"data-maxDifficulty={self.options['max_difficulty']}" else: self.options["max_difficulty"] = "" if "autogradable" in self.options: self.options["autogradable"] = "data-autogradable=true" else: self.options["autogradable"] = "" if "not_seen_ever" in self.options: self.options["not_seen_ever"] = "data-not_seen_ever=true" else: self.options["not_seen_ever"] = "" if "primary" in self.options: self.options["primary"] = "data-primary=true" else: self.options["primary"] = "" if "ab" in self.options: if len(self.question_bank_choices.split(",")) != 2: raise self.severe( "AB questions must have 2 options for :fromid:") self.options["AB"] = f"data-ab={self.options['ab']}" else: self.options["AB"] = "" maybeAddToAssignment(self) res = TEMPLATE.format(**self.options) addHTMLToDB(self.options["divid"], self.basecourse, res) return [nodes.raw(self.block_text, res, format="html")]
def run(self): """ process the fillintheblank directive and generate html for output. :param self: :return: Nodes resulting from this directive. ... """ super(FillInTheBlank, self).run() TEMPLATE_START = """ <div class="%(divclass)s"> <div data-component="fillintheblank" id="%(divid)s" %(optional)s> """ TEMPLATE_END = """ <script type="application/json"> %(json)s </script> </div> </div> """ addQuestionToDB(self) fitbNode = FITBNode(self.options, rawsource=self.block_text) fitbNode.source, fitbNode.line = self.state_machine.get_source_and_line( self.lineno ) fitbNode.template_start = TEMPLATE_START fitbNode.template_end = TEMPLATE_END self.updateContent() self.state.nested_parse(self.content, self.content_offset, fitbNode) env = self.state.document.settings.env self.options["divclass"] = env.config.fitb_div_class # Expected _`structure`, with assigned variable names and transformations made: # # .. code-block:: # :number-lines: # # fitbNode = FITBNode() # Item 1 of problem text # ... # Item n of problem text # feedback_bullet_list = bullet_list() <-- The last element in fitbNode. # feedback_list_item = list_item() <-- Feedback for the first blank. # feedback_field_list = field_list() # feedback_field = field() # feedback_field_name = field_name() <-- Contains an answer. # feedback_field_body = field_body() <-- Contains feedback for this answer. # feedback_field = field() <-- Another answer/feedback pair. # feedback_list_item = bullet_item() <-- Feedback for the second blank. # ...etc. ... # # This becomes a data structure: # # .. code-block:: # :number-lines: # # self.feedbackArray = [ # [ # blankArray # { # blankFeedbackDict: feedback 1 # "regex" : feedback_field_name # (An answer, as a regex; # "regexFlags" : "x" # "i" if ``:casei:`` was specified, otherwise "".) OR # "number" : [min, max] # a range of correct numeric answers. # "feedback": feedback_field_body (after being rendered as HTML) # Provides feedback for this answer. # }, # { # Feedback 2 # Same as above. # } # ], # [ # Blank 2, same as above. # ] # ] # # ...and a transformed node structure: # # .. code-block:: # :number-lines: # # fitbNode = FITBNode() # Item 1 of problem text # ... # Item n of problem text # FITBFeedbackNode(), which contains all the nodes in blank 1's feedback_field_body # ... # FITBFeedbackNode(), which contains all the nodes in blank n's feedback_field_body # self.assert_has_content() feedback_bullet_list = fitbNode.pop() if not isinstance(feedback_bullet_list, nodes.bullet_list): raise self.error( "On line {}, the last item in a fill-in-the-blank question must be a bulleted list.".format( get_node_line(feedback_bullet_list) ) ) for feedback_list_item in feedback_bullet_list.children: assert isinstance(feedback_list_item, nodes.list_item) feedback_field_list = feedback_list_item[0] if len(feedback_list_item) != 1 or not isinstance( feedback_field_list, nodes.field_list ): raise self.error( "On line {}, each list item in a fill-in-the-blank problems must contain only one item, a field list.".format( get_node_line(feedback_list_item) ) ) blankArray = [] for feedback_field in feedback_field_list: assert isinstance(feedback_field, nodes.field) feedback_field_name = feedback_field[0] assert isinstance(feedback_field_name, nodes.field_name) feedback_field_name_raw = feedback_field_name.rawsource # See if this is a number, optinonally followed by a tolerance. try: # Parse the number. In Python 3 syntax, this would be ``str_num, *list_tol = feedback_field_name_raw.split()``. tmp = feedback_field_name_raw.split() str_num = tmp[0] list_tol = tmp[1:] num = ast.literal_eval(str_num) assert isinstance(num, Number) # If no tolerance is given, use a tolarance of 0. if len(list_tol) == 0: tol = 0 else: assert len(list_tol) == 1 tol = ast.literal_eval(list_tol[0]) assert isinstance(tol, Number) # We have the number and a tolerance. Save that. blankFeedbackDict = {"number": [num - tol, num + tol]} except (SyntaxError, ValueError, AssertionError): # We can't parse this as a number, so assume it's a regex. regex = ( # The given regex must match the entire string, from the beginning (which may be preceded by whitespaces) ... r"^\s*" + # ... to the contents (where a single space in the provided pattern is treated as one or more whitespaces in the student's answer) ... feedback_field_name.rawsource.replace(" ", r"\s+") # ... to the end (also with optional spaces). + r"\s*$" ) blankFeedbackDict = { "regex": regex, "regexFlags": "i" if "casei" in self.options else "", } # Test out the regex to make sure it compiles without an error. try: re.compile(regex) except Exception as ex: raise self.error( 'Error when compiling regex "{}": {}.'.format( regex, str(ex) ) ) blankArray.append(blankFeedbackDict) feedback_field_body = feedback_field[1] assert isinstance(feedback_field_body, nodes.field_body) # Append feedback for this answer to the end of the fitbNode. ffn = FITBFeedbackNode( feedback_field_body.rawsource, *feedback_field_body.children, **feedback_field_body.attributes ) ffn.blankFeedbackDict = blankFeedbackDict fitbNode += ffn # Add all the feedback for this blank to the feedbackArray. fitbNode.feedbackArray.append(blankArray) maybeAddToAssignment(self) return [fitbNode]
def run(self): super(SelectQuestion, self).run() addQuestionToDB(self) env = self.state.document.settings.env is_dynamic = env.config.html_context.get("dynamic_pages", False) is_preview = env.config.html_context.get("course_id", None) == "preview" if not (bool("fromid" in self.options) ^ bool("proficiency" in self.options)): raise self.severe( "You must specify either fromid or proficiency but not both") if is_dynamic or is_preview: self.options["message"] = "Loading a dynamic question ..." else: self.options[ "message"] = "The selectquestion directive only works with Runestone Services" if "fromid" in self.options: self.question_bank_choices = self.options["fromid"] self.options[ "selector"] = f"data-questionlist='{self.question_bank_choices}'" self.options[ "message"] += f"<br/>Selecting from: {self.question_bank_choices}" else: self.options["selector"] = "" # todo: validate that question(s) are in the database self.options["component_id"] = self.arguments[0].strip() if "proficiency" in self.options: self.options[ "proficiency"] = f"""data-proficiency='{self.options["proficiency"]}'""" else: self.options["proficiency"] = "" if "points" in self.options: self.int_points = int(self.options["points"]) self.options["points"] = f"data-points={self.options['points']}" else: self.int_points = 1 self.options["points"] = "" if "min_difficulty" in self.options: self.options[ "min_difficulty"] = f"data-minDifficulty={self.options['min_difficulty']}" else: self.options["min_difficulty"] = "" if "max_difficulty" in self.options: self.options[ "max_difficulty"] = f"data-maxDifficulty={self.options['max_difficulty']}" else: self.options["max_difficulty"] = "" if "autogradable" in self.options: self.options["autogradable"] = "data-autogradable=true" else: self.options["autogradable"] = "" if "not_seen_ever" in self.options: self.options["not_seen_ever"] = "data-not_seen_ever=true" else: self.options["not_seen_ever"] = "" if "primary" in self.options: self.options["primary"] = "data-primary=true" else: self.options["primary"] = "" if "ab" in self.options: if len(self.question_bank_choices.split(",")) != 2: raise self.severe( "AB questions must have 2 options for :fromid:") self.options["AB"] = f"data-ab={self.options['ab']}" else: self.options["AB"] = "" if ("toggle" in self.options) or ("togglelabels" in self.options): self.options["toggle_options"] = 'data-toggleoptions="toggle"' self.options["toggle_labels"] = 'data-togglelabels="togglelabels:"' if "toggle" in self.options: self.options["toggle_options"] = ( 'data-toggleoptions="toggle, ' + f"{self.options['toggle']}" + '"') if "togglelabels" in self.options: self.options["toggle_labels"] = ( 'data-togglelabels="togglelabels: ' + f"{self.options['togglelabels']}" + '"') else: self.options["toggle_options"] = "" self.options["toggle_labels"] = "" maybeAddToAssignment(self) res = TEMPLATE.format(**self.options) addHTMLToDB(self.options["divid"], self.basecourse, res) return [nodes.raw(self.block_text, res, format="html")]
def run(self): super(ActiveCode, self).run() env = self.state.document.settings.env # keep track of how many activecodes we have.... # could be used to automatically make a unique id for them. if not hasattr(env, "activecodecounter"): env.activecodecounter = 0 env.activecodecounter += 1 self.options["name"] = self.arguments[0].strip() explain_text = None if self.content: if "~~~~" in self.content: idx = self.content.index("~~~~") explain_text = self.content[:idx] self.content = self.content[idx + 1:] source = "\n".join(self.content) else: source = "\n" self.explain_text = explain_text or ["Not an Exercise"] addQuestionToDB(self) self.options["initialcode"] = source str = source.replace("\n", "*nline*") str0 = str.replace('"', "*doubleq*") str1 = str0.replace("(", "*open*") str2 = str1.replace(")", "*close*") str3 = str2.replace("'", "*singleq*") self.options["argu"] = str3 # TODO: This is BAD -- using '_' as a key for audio tour stuff is wrong. complete = "" no_of_buttons = 0 okeys = list(self.options.keys()) for k in okeys: if "tour_" in k: x, label = k.split("_") no_of_buttons = no_of_buttons + 1 complete = complete + self.options[k] + "*atype*" newcomplete = complete.replace('"', "*doubleq*") self.options["ctext"] = newcomplete self.options["no_of_buttons"] = no_of_buttons if "caption" not in self.options: self.options["caption"] = "" else: self.options[ "caption"] = "data-caption='%s'" % self.options["caption"] if "include" not in self.options: self.options["include"] = "" else: lst = self.options["include"].split(",") lst = [x.strip() for x in lst] self.options["include"] = 'data-include="' + " ".join(lst) + '"' if "hidecode" in self.options: self.options["hidecode"] = 'data-hidecode="true"' else: self.options["hidecode"] = "" if "enabledownload" in self.options: self.options["enabledownload"] = 'data-enabledownload="true"' else: self.options["enabledownload"] = "" if "chatcodes" in self.options: self.options["chatcodes"] = 'data-chatcodes="true"' else: self.options["chatcodes"] = "" if "language" not in self.options: self.options["language"] = "python" if self.options["language"] == "html": self.options["language"] = "htmlmixed" self.options["initialcode"] = escape(self.options["initialcode"]) if "nocodelens" in self.options or self.options["language"] not in [ "python", "java", "c", "cpp", ]: self.options["codelens"] = "" else: self.options["codelens"] = 'data-codelens="true"' if "nopair" in self.options: self.options["nopair"] = 'data-nopair="true"' else: self.options["nopair"] = "" if "timelimit" not in self.options: self.options["timelimit"] = "data-timelimit=25000" else: self.options[ "timelimit"] = "data-timelimit=%s" % self.options["timelimit"] if "autorun" not in self.options: self.options["autorun"] = "" else: self.options["autorun"] = 'data-autorun="true"' if "coach" in self.options: self.options["coach"] = 'data-coach="true"' else: self.options["coach"] = "" # livecode options if "stdin" in self.options: self.options["stdin"] = "data-stdin='%s'" % self.options["stdin"] else: self.options["stdin"] = "" if "datafile" not in self.options: self.options["datafile"] = "" else: self.options[ "datafile"] = "data-datafile='%s'" % self.options["datafile"] if "sourcefile" not in self.options: self.options["sourcefile"] = "" else: self.options["sourcefile"] = ("data-sourcefile='%s'" % self.options["sourcefile"]) if "tie" in self.options: self.options["tie"] = "data-tie='{}'".format(self.options["tie"]) else: self.options["tie"] = "" if "dburl" in self.options: self.options["dburl"] = "data-dburl='{}'".format( self.options["dburl"]) else: self.options["dburl"] = "" for opt, tp in [ ("compileargs", "cargs"), ("linkargs", "largs"), ("runargs", "rargs"), ("interpreterargs", "iargs"), ]: if opt in self.options: self.options[tp] = 'data-{}="{}"'.format( opt, escape(self.options[opt])) else: self.options[tp] = "" if "gradebutton" not in self.options: self.options["gradebutton"] = "" else: self.options["gradebutton"] = "data-gradebutton=true" self.options["divclass"] = env.config.activecode_div_class if env.config.activecode_hide_load_history: self.options["hidehistory"] = "data-hidehistory=true" else: self.options["hidehistory"] = "" if env.config.wasm_uri: self.options["wasmuri"] = f"data-wasm={env.config.wasm_uri}" else: self.options["wasmuri"] = "" if self.content: if "^^^^" in self.content: idx = self.content.index("^^^^") prefix = "\n".join(self.content[:idx]) if "====" in self.content: idx = self.content.index("====") source = "\n".join(self.content[:idx]) suffix = "\n".join(self.content[idx + 1:]) else: source = "\n".join(self.content) suffix = "\n" else: source = "\n" suffix = "\n" course_name = env.config.html_context["course_id"] divid = self.options["divid"] engine, meta, sess = get_engine_meta() if engine: Source_code = Table("source_code", meta, autoload=True, autoload_with=engine) engine.execute( Source_code.delete().where(Source_code.c.acid == divid).where( Source_code.c.course_id == course_name)) engine.execute(Source_code.insert().values( acid=divid, course_id=course_name, main_code=source, suffix_code=suffix, includes=self.options["include"], available_files=self.options.get("available_files", ""), )) else: if (not hasattr(env, "dberr_activecode_reported") or not env.dberr_activecode_reported): env.dberr_activecode_reported = True print( "Unable to save to source_code table in activecode.py. Possible problems:" ) print( " 1. dburl or course_id are not set in conf.py for your book" ) print(" 2. unable to connect to the database using dburl") print("") print( "This should only affect the grading interface. Everything else should be fine." ) acnode = ActivcodeNode(self.options, rawsource=self.block_text) acnode.source, acnode.line = self.state_machine.get_source_and_line( self.lineno) self.add_name( acnode) # make this divid available as a target for :ref: maybeAddToAssignment(self) if explain_text: self.state.nested_parse(explain_text, self.content_offset, acnode) return [acnode]
def run(self): """ Instructions for solving the problem should be written and then a line with ----- signals the beginning of the code. If you want more than one line in a single code block, seperate your code blocks with =====. Both the instructions sections and code blocks are optional. If you don't include any =====, the code will assume you want each line to be its own code block. Example: .. parsonsprob:: unqiue_problem_id_here Solve my really cool parsons problem...if you can. ----- def findmax(alist): ===== if len(alist) == 0: return None ===== curmax = alist[0] for item in alist: ===== if item > curmax: ===== curmax = item ===== return curmax """ super(ParsonsProblem, self).run() addQuestionToDB(self) env = self.state.document.settings.env self.options["instructions"] = "" self.options["code"] = self.content self.options["divclass"] = env.config.parsons_div_class if "numbered" in self.options: self.options["numbered"] = (' data-numbered="' + self.options["numbered"] + '"' ) #' data-numbered="true"' else: self.options["numbered"] = "" if "maxdist" in self.options: self.options[ "maxdist"] = ' data-maxdist="' + self.options["maxdist"] + '"' else: self.options["maxdist"] = "" if "order" in self.options: self.options[ "order"] = ' data-order="' + self.options["order"] + '"' else: self.options["order"] = "" if "noindent" in self.options: self.options["noindent"] = ' data-noindent="true"' else: self.options["noindent"] = "" if "adaptive" in self.options: self.options["adaptive"] = ' data-adaptive="true"' else: self.options["adaptive"] = "" if "language" in self.options: self.options["language"] = (' data-language="' + self.options["language"] + '"') else: self.options["language"] = "" if "-----" in self.content: index = self.content.index("-----") self.options["instructions"] = self.content[:index] self.options["code"] = self.content[index + 1:] else: self.options["instructions"] = ["Arrange the blocks"] if "=====" in self.options["code"]: self.options["code"] = "\n".join(self.options["code"]) self.options["code"] = self.options["code"].replace("=====", "---") else: self.options["code"] = "\n".join(self.options["code"]) self.assert_has_content() maybeAddToAssignment(self) parsons_node = ParsonsNode(self.options, rawsource=self.block_text) parsons_node.source, parsons_node.line = self.state_machine.get_source_and_line( self.lineno) self.state.nested_parse(self.options["instructions"], self.content_offset, parsons_node) # explain_text is a list. return [parsons_node]