From fe9cdc980b73a0c0fab15e82c41a7a5750eb7a66 Mon Sep 17 00:00:00 2001 From: Xinsong Lin Date: Mon, 4 Sep 2023 19:06:50 +0800 Subject: [PATCH] finished core of grading page --- grade.html | 17 +++++++ scripts/compute.js | 2 +- scripts/consts.js | 3 -- scripts/grade.js | 109 +++++++++++++++++++++++++++++++++++++++++++++ scripts/history.js | 2 +- scripts/index.js | 12 +++-- styles/grade.css | 4 ++ test_cases.txt | 10 +++++ 8 files changed, 150 insertions(+), 9 deletions(-) create mode 100644 grade.html create mode 100644 scripts/grade.js create mode 100644 styles/grade.css create mode 100644 test_cases.txt diff --git a/grade.html b/grade.html new file mode 100644 index 0000000..c7f6701 --- /dev/null +++ b/grade.html @@ -0,0 +1,17 @@ + + + + + + flapjs grading + + + + + + +
+ + + + \ No newline at end of file diff --git a/scripts/compute.js b/scripts/compute.js index 456963c..02547af 100644 --- a/scripts/compute.js +++ b/scripts/compute.js @@ -393,7 +393,7 @@ function* run_input_Moore(graph, input, interactive) { * @param {string} input - input string * @param {boolean} interactive - whether to step through and highlight the computation * @returns {Iterable} return a generator that - * if noninteractive, evaluates to the final accept/reject immediately in one step + * if noninteractive, evaluates to the final accept/reject immediately in one step * if interactive, evaluates step by step with highlight */ export function run_input(graph, machine_type, input, interactive=false) { diff --git a/scripts/consts.js b/scripts/consts.js index e2d7c60..e53668c 100644 --- a/scripts/consts.js +++ b/scripts/consts.js @@ -12,9 +12,6 @@ export const RIGHT_BTN = 2; /** @constant {function} EMPTY_FUNCTION - place holder function */ export const EMPTY_FUNCTION = () => {}; -/** @constant {Object} EMPTY_GRAPH - empty graph object */ -export const EMPTY_GRAPH = {}; - /** @constant {float} CLICK_HOLD_TIME - [ms] the maximum time between mousedown and mouseup still considered as click */ export const CLICK_HOLD_TIME = 240; diff --git a/scripts/grade.js b/scripts/grade.js new file mode 100644 index 0000000..414b045 --- /dev/null +++ b/scripts/grade.js @@ -0,0 +1,109 @@ +/** @module grade */ + +import * as compute from './compute.js'; +import * as permalink from './permalink.js'; + +// if not in browser, don't run +if (typeof document !== 'undefined') { + const cur_file_path = window.location.pathname; + if (cur_file_path.includes('grade.html')) { + document.addEventListener('DOMContentLoaded', init); + } +} + +let test_cases = []; +let graph = {}; + +function display_test_cases(test_cases) { + const test_cases_display_area = document.getElementById('display_test_cases'); + while (test_cases_display_area.childElementCount) { // wipe all elements + test_cases_display_area.removeChild(test_cases_display_area.firstChild); + } + for (const [input_string, accepted] of test_cases) { + const test_case_div = document.createElement('div'); + const test_string_span = document.createElement('span'); + const test_result_span = document.createElement('span'); + test_result_span.className = 'result_class'; + test_result_span.id = `${input_string}_result`; + test_string_span.innerText = input_string; + test_result_span.innerText = accepted ? 'accepted' : 'rejected'; + test_case_div.appendChild(test_string_span); + test_case_div.appendChild(test_result_span); + test_cases_display_area.appendChild(test_case_div); + } +} + +/** + * given test cases as plain text, parse the document and store the results in `test_cases` + * @param {string} text - a string consisting of lines of input. every two line specify a test case, with + * the first line being the input string and the second line being accept/reject + */ +function parse_test_cases(text) { + test_cases = []; // clear + const lines = text.trimEnd().split('\n') // if the file ends with a new line, remove it + if (lines.length & 1) { + alert('invalid test file: odd number of lines'); + return; + } + for (let i = 0; i < lines.length; i+=2) { + let accepted; + switch (lines[i+1]) { + case 'accepted': + accepted = true; + break; + case 'rejected': + accepted = false; + break; + default: + alert('invalid test file: keywords other than "accepted" or "rejected"'); + return; + } + test_cases.push([lines[i], accepted]); + } + display_test_cases(test_cases); +} + +function test_all_cases() { + const url_input = document.getElementById('machine_url'); + const url = url_input.value; + const graph_str = url.substring(url.indexOf('#')+1); + let type, graph; + try { + [type, graph] = permalink.deserialize(graph_str); + } catch { + alert('invalid graph') + return; + } + + for (const [input_string, expected] of test_cases) { + // eslint-disable-next-line no-unused-vars + const { value: actual, _ } = compute.run_input(graph, type, input_string).next(); + const color = (expected == actual) ? 'green' : 'red'; + const input_string_result = document.getElementById(`${input_string}_result`); + input_string_result.style.background = color; + } +} + +function upload_test_cases() { + const test_case_uploader = document.getElementById('test_cases'); + const fr = new FileReader(); + fr.onload = () => parse_test_cases(fr.result); + if (test_case_uploader.files.length) { + fr.readAsText(test_case_uploader.files[0]) + } +} + +function init() { + const test_case_uploader = document.getElementById('test_cases'); + test_case_uploader.addEventListener('change', upload_test_cases); + const start_test_btn = document.getElementById('start_test'); + start_test_btn.addEventListener('click', test_all_cases); + const url_input = document.getElementById('machine_url'); + url_input.addEventListener('click', () => { + for (const elem of document.getElementsByClassName('result_class')) { + if (elem.style.background) { + elem.style.background = ''; + } + } + }); +} diff --git a/scripts/history.js b/scripts/history.js index a7a7a23..846e504 100644 --- a/scripts/history.js +++ b/scripts/history.js @@ -26,7 +26,7 @@ export function set_history_keys(machine) { export function get_history() { if (!localStorage.getItem(hist_key)) { hist_ptr = -1, hist_tip = -1; // initialize ptrs - push_history(consts.EMPTY_GRAPH, []); // initialize history + push_history({}, []); // initialize history } // otherwise, already have history written to localstore hist_tip = localStorage.getItem(hist_tip_key); diff --git a/scripts/index.js b/scripts/index.js index 657c235..6408bc3 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -11,11 +11,15 @@ import * as permalink from './permalink.js'; // if not in browser, don't run if (typeof document !== 'undefined') { - document.addEventListener('DOMContentLoaded', init); - window.addEventListener('resize', () => drawing.draw(graph)); + // get current page + const cur_file_path = window.location.pathname; + if (!cur_file_path.includes('grade.html')) { + document.addEventListener('DOMContentLoaded', init); + window.addEventListener('resize', () => drawing.draw(graph)); + } } -let graph = consts.EMPTY_GRAPH; // global graph +let graph = {}; // global graph, initially empty /** handles double click */ function bind_double_click() { @@ -308,7 +312,7 @@ function bind_dd() { if (!Object.keys(graph).length) { // nothing to delete return; } - graph = consts.EMPTY_GRAPH; + graph = {}; drawing.draw(graph); hist.push_history(graph); }); diff --git a/styles/grade.css b/styles/grade.css new file mode 100644 index 0000000..6ddb935 --- /dev/null +++ b/styles/grade.css @@ -0,0 +1,4 @@ +#display_test_cases > div { + display: flex; + justify-content: space-between; +} diff --git a/test_cases.txt b/test_cases.txt new file mode 100644 index 0000000..9f81c38 --- /dev/null +++ b/test_cases.txt @@ -0,0 +1,10 @@ +101 +rejected +01 +accepted + +accepted +0011 +accepted +00111 +rejected \ No newline at end of file