Пример #1
0
    def run(self):
        super(ActiveCode, self).run()

        addQuestionToDB(self)

        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.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("====")
                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:

        if explain_text:
            self.state.nested_parse(explain_text, self.content_offset, acnode)

        return [acnode]
Пример #2
0
    def run(self):
        """
            process the multiplechoice directive and generate html for output.
            :param self:
            :return:
            .. datafile:: identifier
                :edit: Option that makes the datafile editable
                :cols: If editable, number of columns--default is 20
                :rows: If editable, number of rows--default is 40
                :hide: Flag that sets a non-editable datafile to be hidden
                :image:
                :fromfile: path to file that contains the data
        """
        super(DataFile, self).run()
        env = self.state.document.settings.env

        if not hasattr(env, "datafilecounter"):
            env.datafilecounter = 0
        env.datafilecounter += 1
        import os

        if "fromfile" in self.options:
            ffpath = os.path.dirname(self.srcpath)
            filename = os.path.join(env.srcdir, ffpath,
                                    self.options["fromfile"])
            if "image" in self.options:
                self.options["imtype"] = pathlib.Path(filename).suffix[1:]
                with open(filename, "rb") as f:
                    self.content = base64.b64encode(f.read()).decode("utf8")
            else:
                with open(filename, "rb") as f:
                    self.content = [
                        x[:-1].decode("utf8") for x in f.readlines()
                    ]
        if "cols" in self.options:
            self.options["cols"] = self.options["cols"]
        else:
            if "image" not in self.options:
                self.options["cols"] = min(65,
                                           max([len(x) for x in self.content]))
        if "rows" in self.options:
            self.options["rows"] = self.options["rows"]
        else:
            self.options["rows"] = 20

        if "image" in self.options:
            source = self.content
            self.options["image"] = "true"
        elif self.content:
            source = "\n".join(self.content) + "\n"
        else:
            source = "\n"
        self.options["filecontent"] = source

        if "hide" in self.options:
            self.options["hidden"] = "data-hidden"
        else:
            self.options["hidden"] = ""

        if "edit" in self.options:
            self.options["edit"] = "true"
        else:
            self.options["edit"] = "false"

        engine, meta, sess = get_engine_meta()
        if engine:
            Source_code = Table("source_code",
                                meta,
                                autoload=True,
                                autoload_with=engine)
            course_name = env.config.html_context["course_id"]
            divid = self.options["divid"]

            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))
        else:
            print(
                "Unable to save to source_code table in datafile__init__.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."
            )

        data_file_node = DataFileNode(self.options, rawsource=self.block_text)
        (
            data_file_node.source,
            data_file_node.line,
        ) = self.state_machine.get_source_and_line(self.lineno)
        return [data_file_node]
Пример #3
0
def update_database(chaptitles, subtitles, skips, app):
    """
    When the build is completely finished output the information gathered about
    chapters and subchapters into the database.
    """
    engine, meta, sess = get_engine_meta()

    if not sess:
        logger.info(
            "You need to install a DBAPI module - psycopg2 for Postgres")
        logger.info(
            "Or perhaps you have not set your DBURL environment variable")
        return

    chapters = Table("chapters", meta, autoload=True, autoload_with=engine)
    sub_chapters = Table("sub_chapters",
                         meta,
                         autoload=True,
                         autoload_with=engine)
    questions = Table("questions", meta, autoload=True, autoload_with=engine)
    basecourse = app.config.html_context.get("basecourse", "unknown")
    dynamic_pages = app.config.html_context.get("dynamic_pages", False)
    if dynamic_pages:
        cname = basecourse
    else:
        cname = app.env.config.html_context.get("course_id", "unknown")

    logger.info("Cleaning up old chapters info for {}".format(cname))
    sess.execute(chapters.delete().where(chapters.c.course_id == basecourse))

    logger.info("Populating the database with Chapter information")

    chapnum = 1
    for chapnum, chap in enumerate(chaptitles, start=1):
        # insert row for chapter in the chapter table and get the id
        logger.info(u"Adding chapter subchapter info for {}".format(chap))
        ins = chapters.insert().values(
            chapter_name=chaptitles.get(chap, chap),
            course_id=cname,
            chapter_label=chap,
            chapter_num=chapnum,
        )
        res = sess.execute(ins)
        currentRowId = res.inserted_primary_key[0]
        for subchapnum, sub in enumerate(subtitles[chap], start=1):
            if (chap, sub) in skips:
                skipreading = "T"
            else:
                skipreading = "F"
            # insert row for subchapter
            # todo: check if this chapter/subchapter is in the non-reading list
            q_name = u"{}/{}".format(chaptitles.get(chap, chap),
                                     subtitles[chap][sub])
            ins = sub_chapters.insert().values(
                sub_chapter_name=subtitles[chap][sub],
                chapter_id=str(currentRowId),
                sub_chapter_label=sub,
                skipreading=skipreading,
                sub_chapter_num=subchapnum,
            )
            sess.execute(ins)
            # Three possibilities:
            # 1) The chapter and subchapter labels match existing, but the q_name doesn't match; because you changed
            # heading in a file.
            # 2) The chapter and subchapter labels don't match (new file name), but there is an existing q_name match,
            # because you renamed the file
            # 3) Neither match, so insert a new question
            sel = select([questions]).where(
                or_(
                    and_(
                        questions.c.chapter == chap,
                        questions.c.subchapter == sub,
                        questions.c.question_type == "page",
                        questions.c.base_course == basecourse,
                    ),
                    and_(
                        questions.c.name == q_name,
                        questions.c.question_type == "page",
                        questions.c.base_course == basecourse,
                    ),
                ))
            res = sess.execute(sel).first()
            if res and ((res.name != q_name) or (res.chapter != chap) or
                        (res.subchapter != sub)):
                # Something changed
                upd = (questions.update().where(
                    questions.c.id == res["id"]).values(name=q_name,
                                                        chapter=chap,
                                                        from_source="T",
                                                        subchapter=sub))
                sess.execute(upd)
            if not res:
                # this is a new subchapter
                ins = questions.insert().values(
                    chapter=chap,
                    subchapter=sub,
                    question_type="page",
                    from_source="T",
                    name=q_name,
                    timestamp=datetime.datetime.now(),
                    base_course=basecourse,
                )
                sess.execute(ins)