Exemplo n.º 1
0
    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]
Exemplo n.º 2
0
    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]
Exemplo n.º 3
0
    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]
Exemplo n.º 4
0
    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]
Exemplo n.º 5
0
    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]
Exemplo n.º 6
0
    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")]
Exemplo n.º 7
0
    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")]
Exemplo n.º 9
0
    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]
Exemplo n.º 10
0
    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 &gt; 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]