Skip to content

Commit

Permalink
This is a squash of the commits for the version of Examplar deployed …
Browse files Browse the repository at this point in the history
…in Fall 2022, rebased on CPO changes made before Fall 2023.

This includes a hinting change, allowing Examplar to produce hints on
demand, based on semantic mutants of the wheat.
  • Loading branch information
jswrenn authored and Siddhartha Prasad committed Aug 31, 2023
1 parent 458f4fe commit e04e0f0
Show file tree
Hide file tree
Showing 33 changed files with 3,106 additions and 751 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ pyret
test-util/failed-test-screenshots/*
.coverage/
*~
.vscode/**
.nvmrc
*.rdb
142 changes: 142 additions & 0 deletions ASSIGNMENT_CREATION_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Examplar Assignment Creation:

1. Prepare the wheats, chaffs, and mutants.
* Given a spec that requires one or more functions, the
wheat and chaff files are implementation of these
functions. These files should explicitly provide only the
functions required by the spec. E.g., if the functions in
the spec are named `fact` and `fib`, then each of the
wheat/chaff files should have as header:
```
provide { fact: fact, fib: fib }
provide-types *
```
* If any tests are provided within these implementations they
should be commented out
* **Mutants** are the set of chaffs that represent meaningful student misconceptions. These are used to generate hints in the case that
a student writes an invalid test. Each mutant comes pairs with a hint, which are displayed to students if they write a corresponding invalid test.
2. Make folders for wheats, chaffs, and mutants that should be included in Examplar,
note their IDs, and set them (temporarily) to be publicly accessible via link
3. Load the Examplar dummy assignment (https://pyret.cs.brown.edu/assignment/1QIZ_LpROVf4yzWTlfTIJcturEyIs71u_)
and open the JavaScript console. Note: The following Steps 3-6
are to be
done in the JS console, not in the Pyret interaction
window!
4. Compile the wheats by executing
```javascript
const wheats = compileFiles(<wheat source folder id>)
```
5. Click the Run button in the interaction window
6. Compile the chaffs by executing
```javascript
const chaffs = compileFiles(<chaff source folder id>)
```
7. Click the Run button in the interaction window
8. Compile the mutants by executing
```javascript
const mutants = compileFiles(<mutant source folder id>)
```
9. Click the Run button in the interaction window
10. Make a public subdirectory in the assignment folder with the public facing
name for the assignment (e.g. 'docdiff') with subdirectories 'wheat', 'chaff', and 'mutant'.
11. After determining the folder IDs for 'wheat' and 'chaff', execute
```javascript
copyCompiled(wheats, <'wheat' folder ID>, chaffs, <'chaff' folder ID>, mutants, <'mutant' folder ID> )
```
12. Check that the files, which should now have names ending in '.js',
are present in the 'wheat', 'chaff', and 'mutant' folders
13. Remove link sharing from the wheat and chaff source folders from step 1 (optional)
14. In the folder created in step 7, create the following files:
* \<assignment>-code.arr
```
provide *
provide-types *
include my-gdrive("<assignment>-common.arr")
# DO NOT CHANGE ANYTHING ABOVE THIS LINE
#
# You may write implementation-specific tests (e.g., of helper functions) in this file.
fun f(x):
...
end
<other function templates>
```
* \<assignment>-dummy.arr
```
provide *
provide-types *
include my-gdrive("<assignment>-common.arr")
# DO NOT CHANGE ANYTHING ABOVE THIS LINE
fun f(x):
raise("Output Hidden")
end
<other dummy functions>
```
* \<assignment>-common.arr
```
provide *
provide-types *
# DO NOT CHANGE ANYTHING ABOVE THIS LINE
#
# Write data bindings here that you'll need for tests in both <assignment>-code.arr and <assignment>-tests.arr
```
* \<assignment>-tests.arr
```
include my-gdrive("<assignment>-code.arr")
include my-gdrive("<assignment>-common.arr")
# DO NOT CHANGE ANYTHING ABOVE THIS LINE
#
# Write your examples and tests in here. These should not be tests of implementation-specific details (e.g., helper functions).
check:
...
end
```
If your chaffs/wheats defined and provided `fact` and `fib` as described in Step 0, then your tests will involve comparisons of calls to `fact`/`fib` to their expected values, e.g.,
```
check:
fact(5) is 120
fib(11) is 89
end
```
14. Prepare a `hints.json` file for each mutant. This file should have the following form:
```
{
"mutant1name": {
"hint": "hint text",
},
"mutant2name": {
"hint": "hint text",
},
}
```
Ensure this is well formed JSON, and place this file in the folder from step 9.
15. Access the assignment at https://pyret.cs.brown.edu/assignment/<ID of folder from step 9>
18 changes: 16 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ build/web/js/transpile.xml: src/web/js/transpile.xml
cp -r $< $@

build/web/js/beforePyret.js: src/web/js/beforePyret.js
`npm bin`/webpack
./node_modules/.bin/webpack

build/web/js/beforeBlocks.js: src/web/js/beforeBlocks.js
`npm bin`/webpack
Expand All @@ -133,6 +133,15 @@ build/web/js/s-expression-lib.js: node_modules/s-expression/index.js
build/web/js/colorspaces.js: node_modules/colorspaces/colorspaces.js
cp $< $@

build/web/js/lifecycle.js: node_modules/page-lifecycle/dist/lifecycle.es5.js
cp $< $@

build/web/js/jszip.js: node_modules/jszip/dist/jszip.min.js
cp $< $@

build/web/js/FileSaver.js: node_modules/file-saver/dist/FileSaver.min.js
cp $< $@

build/web/js/es6-shim.js: node_modules/es6-shim/es6-shim.min.js
cp $< $@

Expand Down Expand Up @@ -231,7 +240,11 @@ MISC_JS = build/web/js/q.js \
build/web/js/es6-shim.js \
build/web/js/runmode.js \
build/web/js/mousetrap.min.js \
build/web/js/mousetrap-global-bind.min.js
build/web/js/mousetrap-global-bind.min.js \
build/web/js/lifecycle.js \
build/web/js/jszip.js \
build/web/js/FileSaver.js


EDITOR_MISC_JS = build/web/js/q.js \
build/web/js/loader.js \
Expand Down Expand Up @@ -260,6 +273,7 @@ EDITOR_MISC_JS = build/web/js/q.js \
build/web/js/google-apis/drive.js \
build/web/js/google-apis/picker.js \
build/web/js/google-apis/sheets.js \
build/web/js/google-apis/source.js \
build/web/js/authenticate-storage.js

build/web/js/editor-misc.min.js: $(EDITOR_MISC_JS)
Expand Down
2 changes: 1 addition & 1 deletion cpo-standalone.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ requirejs(["pyret-base/js/runtime", "pyret-base/js/post-load-hooks", "pyret-base
interactionsReady.fail(function(err) {
console.error("Couldn't start REPL: ", err);
});
interactionsReady.then(function(result) {
Q.all([interactionsReady,programLoaded]).then(function(result) {
$("#runButton").attr("disabled", false);
$("#runDropdown").attr("disabled", false);
clearInterval($("#loader").data("intervalID"));
Expand Down
28 changes: 28 additions & 0 deletions examplar-annual-rebase.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Each August, Examplar should be rebased on CPO horizon.

Create a new branch named $CURRENT_YEAR:
```
git checkout -b $(date +"%Y")
```

Squash all commits since the last rebase:
```
git reset --soft $(git log --pretty=format:"%H" --grep='implement examplar')
git commit --amend -m "implement examplar
This is a squash of the commits for the version of Examplar deployed in Fall $(date --date='1 year ago' +%Y), rebased on CPO changes made before Fall $(date +"%Y")."
git remote add cpo [email protected]:brownplt/code.pyret.org.git
git fetch cpo
git rebase cpo/horizon
```

Fix the conflicts, ensure everything works, then finally:
```
git rebase --continue
```

git checkout refs/remotes/origin/2021 --
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"es6-shim": "0.35.0",
"eslint": "^6.6.0",
"express": "^4.1.1",
"file-saver": "^2.0.2",
"git-rev-sync": "^1.11.1",
"googleapis": "^46.0.0",
"handlebars": "^4.7.6",
Expand All @@ -41,6 +42,7 @@
"js-md5": "^0.4.1",
"js-sha256": "^0.5.0",
"js-yaml": "^3.13.1",
"jszip": "^3.2.2",
"jwt-simple": "^0.5.3",
"load-script": "^1.0.0",
"lodash": "^4.17.19",
Expand All @@ -49,6 +51,7 @@
"mustache": "^2.2.1",
"mustache-express": "^1.2.1",
"node-uuid": "^1.4.1",
"page-lifecycle": "^0.1.2",
"popper.js": "^1.16.0",
"pyret-codemirror-mode": "git://github.com/brownplt/pyret-codemirror-mode.git#master",
"pyret-lang": "git+https://github.com/brownplt/pyret-lang.git#horizon",
Expand Down
7 changes: 5 additions & 2 deletions src/google-auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var OAuth2 = gapi.auth.OAuth2;

var DEFAULT_OAUTH_SCOPES = [
"email",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/drive.file",
"https://www.googleapis.com/auth/drive.install",
];
Expand Down Expand Up @@ -47,12 +48,14 @@ function makeAuth(config) {
// popping up a dialog every half hour)
access_type: 'offline',
// Skip permission confirmation if the user has confirmed with us before
approval_prompt: 'auto',
//approval_prompt: 'auto',
// NOTE(joe): We do not use the drive scope on the server, but we ask
// for it so that we don't have to do another popup on the client.
// #notpola
scope: scopes.join(' '),
state: afterUrl
state: afterUrl,
hd: "brown.edu",
prompt: "select_account",
});
},
serveRedirect: function(req, callback) {
Expand Down
2 changes: 1 addition & 1 deletion src/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var res = Q.fcall(function(db) {
clientSecret: process.env["GOOGLE_CLIENT_SECRET"],
redirect: "/oauth2callback"
},
version: process.env["CURRENT_PYRET_RELEASE"],
version: new Date().getTime(),
pyret: process.env["PYRET"]
}, function(app) {
console.log("Server ready.");
Expand Down
51 changes: 35 additions & 16 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ function start(config, onServerReady) {

// From http://stackoverflow.com/questions/7185074/heroku-nodejs-http-to-https-ssl-forced-redirect
/* At the top, with other redirect methods before other routes */
app.get('*',function(req,res,next){
if(req.headers['x-forwarded-proto'] !== 'https' && !config.development)
res.redirect(config.baseUrl + req.url);
else
next(); /* Continue to other routes if we're not redirecting */
})
//app.get('*',function(req,res,next){
// if(req.headers['x-forwarded-proto'] !== 'https' && !config.development)
// res.redirect(config.baseUrl + req.url);
// else
// next(); /* Continue to other routes if we're not redirecting */
//})

app.get("/__pyret-compiler", function(req, res) {
request.get(config.pyret).pipe(res);
Expand Down Expand Up @@ -524,16 +524,35 @@ function start(config, onServerReady) {
});

app.get("/editor", function(req, res) {
res.render("editor.html", {
PYRET: process.env.PYRET,
BASE_URL: config.baseUrl,
GOOGLE_API_KEY: config.google.apiKey,
GOOGLE_APP_ID: config.google.appId,
CSRF_TOKEN: req.csrfToken(),
LOG_URL: config.logURL,
GIT_REV : config.gitRev,
GIT_BRANCH: config.gitBranch,
POSTMESSAGE_ORIGIN: process.env.POSTMESSAGE_ORIGIN
var u = requireLogin(req, res);
u.then(function(user) {
res.render("editor.html", {
BASE_URL: config.baseUrl,
GOOGLE_API_KEY: config.google.apiKey,
CSRF_TOKEN: req.csrfToken(),
CURRENT_VERSION: config.version,
LOG_URL: config.logURL,
GIT_REV : config.gitRev,
GIT_BRANCH: config.gitBranch,
POSTMESSAGE_ORIGIN: process.env.POSTMESSAGE_ORIGIN
});
});
});

app.get("/assignment/:id", function(req, res) {
var u = requireLogin(req, res);
u.then(function(user) {
res.render("editor.html", {
BASE_URL: config.baseUrl,
ASSIGNMENT_ID: req.params.id,
GOOGLE_API_KEY: config.google.apiKey,
CSRF_TOKEN: req.csrfToken(),
CURRENT_VERSION: config.version,
LOG_URL: config.logURL,
GIT_REV : config.gitRev,
GIT_BRANCH: config.gitBranch,
POSTMESSAGE_ORIGIN: process.env.POSTMESSAGE_ORIGIN
});
});
});

Expand Down
Loading

0 comments on commit e04e0f0

Please sign in to comment.