diff --git a/sensei-grading-bookmarklet/README.md b/sensei-grading-bookmarklet/README.md index 7e2e66f..3a18534 100644 --- a/sensei-grading-bookmarklet/README.md +++ b/sensei-grading-bookmarklet/README.md @@ -1,7 +1,14 @@ # `sensei-grading-bookmarklet` +[![Bookmarklet Size][bookmarklet_size_badge]][bookmarklet_file] + > A bookmarklet to enhance the grading experience of code ninja assignments. +## Purpose + +Grading code ninjas' submissions is slow and tedious because the current site has a poor user experience. +My goal is to make an entirely keyboard-navigable enhancement in the form of a bookmarklet. + ## Usage 1. Visit the [grading console][grading_console] while logged in as a code sensei. @@ -10,6 +17,16 @@ 1. Click enter to execute the bookmarklet code. 1. Grade. +## Keybinds + +| Key | Action | +| ---------------- | ------------------------------ | +| i | Marks assignment as incomplete | +| 1 | Rates assignment as 1-star | +| 2 | Rates assignment as 2-star | +| 3 | Rates assignment as 3-star | +| enter | Submits assigment grade | + ## Development This [bookmarklet][bookmarklet_definition] is compiled using [NodeJS][node_installation]. @@ -18,11 +35,6 @@ Upon cloning this project, execute `npm i` to install of the necessary dependenc To run in development mode, run `npm run dev`. To generate the bookmarklet, run `npm start`. -## Purpose - -Grading code ninjas' submissions is slow and tedious because the current site has a poor user experience. -My goal is to make an entirely keyboard-navigable enhancement in the form of a bookmarklet. - ## Security For security reasons, modern browsers will make you type out the prefix (`javascript:`) manually. @@ -32,3 +44,5 @@ This code, however, is non-malicious, so no worries in this case. [grading_console]: https://gdp.code.ninja/Grading [bookmarklet_definition]: https://en.wikipedia.org/wiki/Bookmarklet [node_installation]: https://nodejs.org/en/download/ +[bookmarklet_size_badge]: https://img.badgesize.io/EthanThatOneKid/code-sensei/main/sensei-grading-bookmarklet/bookmarklet.txt +[bookmarklet_file]: bookmarklet.txt diff --git a/sensei-grading-bookmarklet/app.js b/sensei-grading-bookmarklet/app.js index 4801d7b..408932a 100644 --- a/sensei-grading-bookmarklet/app.js +++ b/sensei-grading-bookmarklet/app.js @@ -1,3 +1,112 @@ // https://gist.github.com/EthanThatOneKid/95e8393c0afc1a35efda6f514d4ee6ea -alert("Hello, World!"); +const selectors = { + incompleteBtn: "#IsIncompleteYes", + notesTextarea: + "#gradingNode > div > div:nth-child(1) > table > tbody > tr:nth-child(8) > td:nth-child(2) > textarea", + star1: + "#gradingNode > div > div:nth-child(1) > table > tbody > tr:nth-child(7) > td:nth-child(2) > span.pull-left.ml-3.mt-1.stars > i:nth-child(1)", + star2: + "#gradingNode > div > div:nth-child(1) > table > tbody > tr:nth-child(7) > td:nth-child(2) > span.pull-left.ml-3.mt-1.stars > i:nth-child(2)", + star3: + "#gradingNode > div > div:nth-child(1) > table > tbody > tr:nth-child(7) > td:nth-child(2) > span.pull-left.ml-3.mt-1.stars > i:nth-child(3)", + submit: "#grade-form > button", + previewContainer: "#gradingNode > div > div:nth-child(2)", + assignmentBtn: + "body > div.body-content > div:nth-child(2) > div:nth-child(3) > table > tbody > tr > td:nth-child(7) > a", + assignmentLink: "#gradingNode > div > div:nth-child(2) > div > a", +}; + +let currentAssignmentOnPage = 0; + +const defaultIdleTimeout = 5e3, + assignmentLoadDelay = 1e3; + +const setIdleTimeout = (...args) => { + let timeoutId = setTimeout(...args); + const alertUserAction = () => { + clearTimeout(timeoutId); + timeoutId = setTimeout(...args); + }; + return alertUserAction; +}; + +const actions = { + incomplete() { + // Sensei declares game incomplete. + document.querySelector(selectors.incompleteBtn).click(); + const textarea = document.querySelector(selectors.notesTextarea); + textarea.focus(); + const updateTextarea = setIdleTimeout( + () => document.querySelector(selectors.submit).focus(), + defaultIdleTimeout + ); + textarea.addEventListener("keypress", updateTextarea); + }, + rate(stars) { + // Sensei gives a rating out of 3 stars. + const selectorKey = `star${stars}`; + if (selectorKey in selectors) { + document.querySelector(selectors[selectorKey]).click(); + this.submit(); + } + }, + submit() { + document.querySelector(selectors.submit).click(); + currentAssignmentOnPage++; + this.nextAssignment(); + }, + nextAssignment() { + document + .querySelectorAll(selectors.assignmentBtn) + [currentAssignmentOnPage].click(); + setTimeout(() => { + const preview = document.createElement("iframe"); + preview.width = "1600px"; + preview.height = "1400px"; + preview.src = document.querySelector(selectors.assignmentLink).href; + document.querySelector(selectors.previewContainer).innerHTML = ""; + document.querySelector(selectors.previewContainer).appendChild(preview); + document + .querySelector(selectors.previewContainer) + .parentElement.scrollIntoView(); + }, assignmentLoadDelay); + }, +}; + +document.addEventListener("keypress", (event) => { + try { + switch (event.key) { + case "i": + actions.incomplete(); + break; + + case "1": + actions.rate(1); + break; + + case "2": + actions.rate(2); + break; + + case "3": + actions.rate(3); + break; + + case "enter": + actions.submit(); + break; + + default: + break; + } + } catch (error) { + console.log(`Sensei Error: ${error}`); + } +}); + +const main = () => { + actions.nextAssignment(); +}; + +main(); diff --git a/sensei-grading-bookmarklet/bookmarklet.txt b/sensei-grading-bookmarklet/bookmarklet.txt new file mode 100644 index 0000000..f2fbc64 --- /dev/null +++ b/sensei-grading-bookmarklet/bookmarklet.txt @@ -0,0 +1 @@ +javascript:(function()%7B%22use%20strict%22%3Bvar%20selectors%3D%7BincompleteBtn%3A%22%23IsIncompleteYes%22%2CnotesTextarea%3A%22%23gradingNode%20%3E%20div%20%3E%20div%3Anth-child(1)%20%3E%20table%20%3E%20tbody%20%3E%20tr%3Anth-child(8)%20%3E%20td%3Anth-child(2)%20%3E%20textarea%22%2Cstar1%3A%22%23gradingNode%20%3E%20div%20%3E%20div%3Anth-child(1)%20%3E%20table%20%3E%20tbody%20%3E%20tr%3Anth-child(7)%20%3E%20td%3Anth-child(2)%20%3E%20span.pull-left.ml-3.mt-1.stars%20%3E%20i%3Anth-child(1)%22%2Cstar2%3A%22%23gradingNode%20%3E%20div%20%3E%20div%3Anth-child(1)%20%3E%20table%20%3E%20tbody%20%3E%20tr%3Anth-child(7)%20%3E%20td%3Anth-child(2)%20%3E%20span.pull-left.ml-3.mt-1.stars%20%3E%20i%3Anth-child(2)%22%2Cstar3%3A%22%23gradingNode%20%3E%20div%20%3E%20div%3Anth-child(1)%20%3E%20table%20%3E%20tbody%20%3E%20tr%3Anth-child(7)%20%3E%20td%3Anth-child(2)%20%3E%20span.pull-left.ml-3.mt-1.stars%20%3E%20i%3Anth-child(3)%22%2Csubmit%3A%22%23grade-form%20%3E%20button%22%2CpreviewContainer%3A%22%23gradingNode%20%3E%20div%20%3E%20div%3Anth-child(2)%22%2CassignmentBtn%3A%22body%20%3E%20div.body-content%20%3E%20div%3Anth-child(2)%20%3E%20div%3Anth-child(3)%20%3E%20table%20%3E%20tbody%20%3E%20tr%20%3E%20td%3Anth-child(7)%20%3E%20a%22%2CassignmentLink%3A%22%23gradingNode%20%3E%20div%20%3E%20div%3Anth-child(2)%20%3E%20div%20%3E%20a%22%7D%2CcurrentAssignmentOnPage%3D0%2CdefaultIdleTimeout%3D5e3%2CassignmentLoadDelay%3D1e3%2CsetIdleTimeout%3Dfunction()%7Bfor(var%20e%3Darguments.length%2Ct%3DArray(e)%2Cn%3D0%3Bn%3Ce%3Bn%2B%2B)t%5Bn%5D%3Darguments%5Bn%5D%3Bvar%20i%3DsetTimeout.apply(void%200%2Ct)%3Breturn%20function()%7BclearTimeout(i)%2Ci%3DsetTimeout.apply(void%200%2Ct)%7D%7D%2Cactions%3D%7Bincomplete%3Afunction()%7Bdocument.querySelector(selectors.incompleteBtn).click()%3Bvar%20e%3Ddocument.querySelector(selectors.notesTextarea)%3Be.focus()%3Bvar%20t%3DsetIdleTimeout(function()%7Breturn%20document.querySelector(selectors.submit).focus()%7D%2CdefaultIdleTimeout)%3Be.addEventListener(%22keypress%22%2Ct)%7D%2Crate%3Afunction(e)%7Be%3D%22star%22%2Be%3Be%20in%20selectors%26%26(document.querySelector(selectors%5Be%5D).click()%2Cthis.submit())%7D%2Csubmit%3Afunction()%7Bdocument.querySelector(selectors.submit).click()%2CcurrentAssignmentOnPage%2B%2B%2Cthis.nextAssignment()%7D%2CnextAssignment%3Afunction()%7Bdocument.querySelectorAll(selectors.assignmentBtn)%5BcurrentAssignmentOnPage%5D.click()%2CsetTimeout(function()%7Bvar%20e%3Ddocument.createElement(%22iframe%22)%3Be.width%3D%221600px%22%2Ce.height%3D%221400px%22%2Ce.src%3Ddocument.querySelector(selectors.assignmentLink).href%2Cdocument.querySelector(selectors.previewContainer).innerHTML%3D%22%22%2Cdocument.querySelector(selectors.previewContainer).appendChild(e)%2Cdocument.querySelector(selectors.previewContainer).parentElement.scrollIntoView()%7D%2CassignmentLoadDelay)%7D%7D%3Bdocument.addEventListener(%22keypress%22%2Cfunction(e)%7Btry%7Bswitch(e.key)%7Bcase%22i%22%3Aactions.incomplete()%3Bbreak%3Bcase%221%22%3Aactions.rate(1)%3Bbreak%3Bcase%222%22%3Aactions.rate(2)%3Bbreak%3Bcase%223%22%3Aactions.rate(3)%3Bbreak%3Bcase%22enter%22%3Aactions.submit()%7D%7Dcatch(e)%7Bconsole.log(%22Sensei%20Error%3A%20%22%2Be)%7D%7D)%3Bvar%20main%3Dfunction()%7Bactions.nextAssignment()%7D%3Bmain()%3B%7D)() \ No newline at end of file diff --git a/sensei-grading-bookmarklet/index.js b/sensei-grading-bookmarklet/index.js index 4d08cdd..2b63145 100644 --- a/sensei-grading-bookmarklet/index.js +++ b/sensei-grading-bookmarklet/index.js @@ -1,6 +1,5 @@ -// https://github.com/mrcoles/bookmarklet const fs = require("fs"); -const bookmarklet = require("bookmarklet"); +const bookmarklet = require("bookmarklet"); // https://github.com/mrcoles/bookmarklet fs.readFile("./app.js", "utf8", (error, content) => { if (error !== null) { @@ -8,5 +7,7 @@ fs.readFile("./app.js", "utf8", (error, content) => { } const { code, options } = bookmarklet.parseFile(content); const dist = bookmarklet.convert(code, options); - console.log(`Here is your bookmarklet:\n${dist}`); + fs.writeFile("bookmarklet.txt", dist, undefined, () => { + console.log(`Here is your bookmarklet:\n${dist}`); + }); });