diff --git a/bases/rsptx/interactives/package-lock.json b/bases/rsptx/interactives/package-lock.json index 553b78345..a70f4567f 100644 --- a/bases/rsptx/interactives/package-lock.json +++ b/bases/rsptx/interactives/package-lock.json @@ -24,7 +24,7 @@ }, "devDependencies": { "compression-webpack-plugin": "^9.0.0", - "copy-webpack-plugin": "^9.0.0", + "copy-webpack-plugin": "^9.1.0", "css-loader": "^6.0.0", "css-minimizer-webpack-plugin": "^3.0.0", "html-loader": "^3.0.0", diff --git a/bases/rsptx/interactives/package.json b/bases/rsptx/interactives/package.json index c2ab2010f..501ffcde4 100644 --- a/bases/rsptx/interactives/package.json +++ b/bases/rsptx/interactives/package.json @@ -18,7 +18,7 @@ "license": "ISC", "devDependencies": { "compression-webpack-plugin": "^9.0.0", - "copy-webpack-plugin": "^9.0.0", + "copy-webpack-plugin": "^9.1.0", "css-loader": "^6.0.0", "css-minimizer-webpack-plugin": "^3.0.0", "html-loader": "^3.0.0", diff --git a/bases/rsptx/interactives/runestone/activecode/activecode.py b/bases/rsptx/interactives/runestone/activecode/activecode.py index 74ae74307..675ac6226 100644 --- a/bases/rsptx/interactives/runestone/activecode/activecode.py +++ b/bases/rsptx/interactives/runestone/activecode/activecode.py @@ -103,7 +103,7 @@ def setup(app): %(hidecode)s %(include)s %(timelimit)s %(coach)s %(codelens)s %(enabledownload)s %(chatcodes)s %(optional)s data-audio='%(ctext)s' %(sourcefile)s %(datafile)s %(stdin)s %(tie)s %(dburl)s %(nopair)s %(cargs)s %(largs)s %(rargs)s %(iargs)s %(gradebutton)s %(caption)s %(hidehistory)s %(wasmuri)s - %(showlastsql)s style="visibility: hidden;"> + %(showlastsql)s %(python3_interpreter)s style="visibility: hidden;"> %(initialcode)s @@ -224,6 +224,7 @@ class ActiveCode(RunestoneIdDirective): :nopair: -- disable pair programming features :dburl: url to load database for sql mode :showlastsql: -- Only show the last sql result in output + :python3_interpreter: brython (uses brython as interpreter of python3) If this is a homework problem instead of an example in the text then the assignment text should go here. The assignment text ends with @@ -279,6 +280,7 @@ class ActiveCode(RunestoneIdDirective): "nopair": directives.flag, "dburl": directives.unchanged, "showlastsql": directives.flag, + "python3_interpreter": directives.unchanged, } ) @@ -337,6 +339,11 @@ def run(self): self.options["ctext"] = newcomplete self.options["no_of_buttons"] = no_of_buttons + if ("python3_interpreter" in self.options) and (self.options["language"]=="python3") : + self.options["python3_interpreter"] = "data-python3_interpreter='%s'" % self.options["python3_interpreter"] + else: + self.options["python3_interpreter"] = "" + if "caption" not in self.options: self.options["caption"] = "" else: diff --git a/bases/rsptx/interactives/runestone/activecode/js/acfactory.js b/bases/rsptx/interactives/runestone/activecode/js/acfactory.js index 2345b52fd..cf6103b5f 100644 --- a/bases/rsptx/interactives/runestone/activecode/js/acfactory.js +++ b/bases/rsptx/interactives/runestone/activecode/js/acfactory.js @@ -2,6 +2,7 @@ import { ActiveCode } from "./activecode.js"; import JSActiveCode from "./activecode_js.js"; import HTMLActiveCode from "./activecode_html.js"; import SQLActiveCode from "./activecode_sql.js"; +import BrythonActiveCode from "./activecode_brython.js"; import LiveCode from "./livecode.js"; import { TimedActiveCode, @@ -9,6 +10,7 @@ import { TimedJSActiveCode, TimedHTMLActiveCode, TimedSQLActiveCode, + TimedBrythonActiveCode, } from "./timed_activecode"; import "../../common/js/jquery.highlight.js"; @@ -30,7 +32,12 @@ export default class ACFactory { if (lang === undefined) { lang = $(opts.orig).find("[data-lang]").data("lang"); } + var text_area = $(opts.orig).find("textarea")[0] + var python3_interpreter = $(text_area).attr("data-python3_interpreter"); if (opts.timed == true) { + if(python3_interpreter==="brython"){ + return new TimedBrythonActiveCode(opts); + } if (lang === "python") { return new TimedActiveCode(opts); } else if ( @@ -40,7 +47,10 @@ export default class ACFactory { lang === "python3" ) { return new TimedLiveCode(opts); - } else if (lang === "javascript") { + } else if ((lang ==="python3") && (python3_interpreter === "brython")){ + return new BrythonActiveCode(opts); + } + else if (lang === "javascript") { return new TimedJSActiveCode(opts); } else if (lang === "htmlmixed") { return new TimedHTMLActiveCode(opts); diff --git a/bases/rsptx/interactives/runestone/activecode/js/activecode.js b/bases/rsptx/interactives/runestone/activecode/js/activecode.js index 362d28216..a881130a8 100755 --- a/bases/rsptx/interactives/runestone/activecode/js/activecode.js +++ b/bases/rsptx/interactives/runestone/activecode/js/activecode.js @@ -78,6 +78,7 @@ export class ActiveCode extends RunestoneBase { this.question = $(opts.orig).find(`#${this.divid}_question`)[0]; this.tie = $(orig).data("tie"); this.dburl = $(orig).data("dburl"); + this.python3_interpreter = $(orig).data("python3_interpreter"); this.runButton = null; this.enabledownload = $(orig).data("enabledownload"); this.downloadButton = null; @@ -364,7 +365,7 @@ export class ActiveCode extends RunestoneBase { $(butt).click(this.createGradeSummary.bind(this)); } - addDownloadButton(ctrlDiv) { + addDownloadButton(ctrlDiv) { let butt = document.createElement("button"); $(butt).text("Download"); $(butt).addClass("btn save-button"); @@ -372,7 +373,7 @@ export class ActiveCode extends RunestoneBase { this.downloadButton = butt; $(butt).click(this.downloadFile.bind(this, this.language)); $(butt).attr("type", "button"); - } + } enableHideShow(ctrlDiv) { $(this.runButton).attr("disabled", "disabled"); diff --git a/bases/rsptx/interactives/runestone/activecode/js/activecode_brython.js b/bases/rsptx/interactives/runestone/activecode/js/activecode_brython.js new file mode 100644 index 000000000..833bf445d --- /dev/null +++ b/bases/rsptx/interactives/runestone/activecode/js/activecode_brython.js @@ -0,0 +1,130 @@ +import { ActiveCode } from "./activecode.js"; + +export default class BrythonActiveCode extends ActiveCode { + constructor(opts) { + super(opts); + opts.alignVertical = true; + this.python3_interpreter = $(orig).data("python3_interpreter"); + $(this.runButton).text("Render"); + this.editor.setValue(this.code); + } + + async runProg() { + var prog = await this.buildProg(true); + let saveCode = "True"; + this.saveCode = await this.manage_scrubber(saveCode); + $(this.output).text(""); + if (!this.alignVertical) { + $(this.codeDiv).switchClass("col-md-12", "col-md-6", { + duration: 500, + queue: false, + }); + } + $(this.outDiv).show({ duration: 700, queue: false }); + prog = ` + +
+ + + + + + + +
+
+
+
+
+
+
+ `;
+ this.output.srcdoc = prog;
+ }
+
+ createOutput() {
+ this.alignVertical = true;
+ var outDiv = document.createElement("div");
+ $(outDiv).addClass("ac_output");
+ if (this.alignVertical) {
+ $(outDiv).addClass("col-md-12");
+ } else {
+ $(outDiv).addClass("col-md-5");
+ }
+ this.outDiv = outDiv;
+ this.output = document.createElement("iframe");
+ $(this.output).css("background-color", "white");
+ $(this.output).css("position", "relative");
+ $(this.output).css("height", "400px");
+ $(this.output).css("width", "100%");
+ outDiv.appendChild(this.output);
+ this.outerDiv.appendChild(outDiv);
+ var clearDiv = document.createElement("div");
+ $(clearDiv).css("clear", "both"); // needed to make parent div resize properly
+ this.outerDiv.appendChild(clearDiv);
+ }
+ enableSaveLoad() {
+ $(this.runButton).text($.i18n("msg_activecode_render"));
+ }
+}
\ No newline at end of file
diff --git a/bases/rsptx/interactives/runestone/activecode/js/timed_activecode.js b/bases/rsptx/interactives/runestone/activecode/js/timed_activecode.js
index 56ae9e565..3f792627f 100644
--- a/bases/rsptx/interactives/runestone/activecode/js/timed_activecode.js
+++ b/bases/rsptx/interactives/runestone/activecode/js/timed_activecode.js
@@ -8,6 +8,7 @@ import { ActiveCode } from "./activecode";
import JSActiveCode from "./activecode_js";
import HTMLActiveCode from "./activecode_html";
import SQLActiveCode from "./activecode_sql";
+import BrythonActiveCode from "./activecode_brython.js";
var TimedActiveCodeMixin = {
timedInit: async function (opts) {
@@ -143,3 +144,11 @@ export class TimedSQLActiveCode extends SQLActiveCode {
}
}
Object.assign(TimedSQLActiveCode.prototype, TimedActiveCodeMixin);
+
+export class TimedBrythonActiveCode extends BrythonActiveCode {
+ constructor(opts) {
+ super(opts);
+ this.timedInit(opts);
+ }
+}
+Object.assign(TimedBrythonActiveCode.prototype, TimedActiveCodeMixin);
diff --git a/bases/rsptx/interactives/runestone/activecode/test/_sources/index.rst b/bases/rsptx/interactives/runestone/activecode/test/_sources/index.rst
index bf99f40f0..128c61e3b 100644
--- a/bases/rsptx/interactives/runestone/activecode/test/_sources/index.rst
+++ b/bases/rsptx/interactives/runestone/activecode/test/_sources/index.rst
@@ -2834,3 +2834,18 @@ Support for SQL in the browser ? Yes!
}
} // end of World class
+
+Trying Brython as Python 3 interpreter
+--------------------------------------
+.. activecode:: test_activecode_python3
+ :language: python3
+ :python3_interpreter: brython
+
+ print("You can see this print inside the iframe console")
+ from browser import document, alert, html
+
+ def hello(ev):
+ alert("Hello! I'm using Brython :D")
+
+ document <= html.BUTTON("My button", id="button_alert")
+ document["button_alert"].bind("click", hello)
diff --git a/sample.env b/sample.env
deleted file mode 100644
index 6098f307b..000000000
--- a/sample.env
+++ /dev/null
@@ -1,85 +0,0 @@
-# Copy this file to .env docker-compose will read and use the values from here.
-# The build.py script is also aware of this file and if you run `poetry self add
-# poetry-dotenv-plugin` when you run `poetry shell` it will also pick up these
-# variables and add them to your environment. Note: once an environment variable is exported to
-# your environment, modifying the value of that variable in the .env file will have no effect.
-# You can use the ``unset`` command in bash or ``set -e`` in fish.
-
-# This will ensure that the postgresql database defined in the docker-compose.yml file is started
-# to run your own, comment out or change the line below. (see pgbouncer or nginx_dstart_dev)
-# available profiles include:
-# basic - adds the db service to the composed app
-# production - adds pgbouncer
-# dev - adds the nginx_dstart_dev service
-# author - adds the author and worker services
-
-COMPOSE_PROFILES=basic
-
-# Where is the runestone app located (inside the container)
-RUNESTONE_PATH = /usr/local/lib/python3.10/site-packages/rsptx/web2py_server/applications/runestone
-
-# Database URLs: You may want to have a couple of different environment
-# variables set up that essentially point to the same database. Therefore we use
-# one set of variables for use within docker, prefixed with DC_ and another pair
-# for use outside of docker. The DC variants should only be set in this file for
-# use with docker-compose. (If you don't set the DC_ variables, docker compose
-# will fall back to the other pair.)
-
-DBURL = postgresql://runestone:runestone@localhost/runestone
-DC_DBURL = postgresql://runestone:runestone@host.docker.internal/runestone
-
-DEV_DBURL = postgresql://runestone:runestone@localhost:2345/runestone_dev
-DC_DEV_DBURL = postgresql://runestone:runestone@db/runestone_dev
-
-
-# Needed for pgbouncer
-# set these for use with pgbouncer in docker, mostly for production w/load balancer
-# but needed so that pgbouncer startup does not fail.
-POSTGRESQL_HOST=db # or host.docker.internal or whatever
-POSTGRESQL_DATABASE=runestone_dev
-PGBOUNCER_DATABASE=runestone_dev
-# These should be set to the username and password for the runestone user in the database
-# These are also used by the db service to create a user for the dockerized postgresql
-POSTGRESQL_USERNAME=runestone
-POSTGRESQL_PASSWORD=runestone
-# If you use pgbouncer then you will need to adjust your ``DC_DEV_DBURL`` and/or ``DC_DBURL`` to connect to pgbouncer not the database.
-# DC_DEV_DBURL=postgresql://${POSTGRESQL_USERNAME}:${POSTGRESQL_PASSWORD}@pgbouncer:6432/${PGBOUNCER_DATABASE}
-
-
-# for single configurations it is fine to use the redis configured in docker-compose
-# however, for load balanced configurations you want to run a redis server that is
-# shared by all the workers.
-REDIS_HOST = redis
-
-# Server configuration (production, development, or test)
-# You should not set these to test as that is for our testing framework, not for
-# people who are just testing out Runestone.
-SERVER_CONFIG=development
-WEB2PY_CONFIG=development
-
-# The path to runestone books (on the host) In the container is set to /books
-BOOK_PATH=~/Runestone/books
-
-## !! change these if running a real server !!
-# This replaces the private/auth.key file for web2py
-WEB2PY_PRIVATE_KEY = sha512:24c4e0f1-df85-44cf-87b9-67fc714f5653
-# This is the secret key for the javascript web token
-JWT_SECRET = supersecret
-
-# Set up host names
-# localhost is ok for development, but you should set this to the real hostname
-# if running a remote development server or definitely for production
-RUNESTONE_HOST = localhost
-# for production where you run a front end load balancer
-# LOAD_BALANCER_HOST = localhost
-
-# If you want nginx to install a certificate
-# CERTBOT_EMAIL = myemail@foo.com
-
-# For setting production (or development) runtime parameters
-# any UVICORN options can be set as an environment variable using the UVICORN_ prefix
-# for gunicorn we can add additional runtime parameters with the GUNICORN_CMD_ARGS variable
-# for uvicorn in production recommend 2 * cores + 1
-UVICORN_WORKERS=5
-GUNICORN_CMD_ARGS="--workers=3 --log-level 'debug'"
-