diff --git a/check.sh b/check.sh index 58e5148dcb0e300055908f5bd828eb3e4a6d4098..d1b7d376cd685a446d27e0a0949d0b7b295ee4a3 100755 --- a/check.sh +++ b/check.sh @@ -27,4 +27,4 @@ mv output/Pictures output/screenshots > /dev/null # Compile screenshots into HTML file to display python3 ~/scripts/screenshots.py > /dev/null -cp student_output.html /home/student_output.html +cp screenshots.html /tmp/screenshots.html diff --git a/mark.sh b/mark.sh index 1c7bc18f3de6363236bb6cd1ed84989f77b3dfdc..8e3d710cd35d5acec6f1949b31149e8feb58b5f8 100755 --- a/mark.sh +++ b/mark.sh @@ -34,7 +34,7 @@ mv output/Pictures output/screenshots >> /home/output.log # Compile screenshots into HTML file to display python3 ~/scripts/screenshots.py >> /home/output.log cp /home/html-output-index.html output/start-here.html >> /home/output.log -zip -FS -r /tmp/output_html.zip output/*/html/ output/start-here.html >> /home/output.log -zip /tmp/student_output.zip student_output.html >> /home/output.log +#zip -FS -r /tmp/output_html.zip output/*/html/ output/start-here.html >> /home/output.log +#zip /tmp/student_output.zip student_output.html >> /home/output.log echo "$(cat output.json)" diff --git a/scripts/screenshots.py b/scripts/screenshots.py index 1ef8cbe7617183b4668dace93e42b50d0b9fb7a1..1ac40038116800e21e8d3f11c8e691cc9ac285d7 100755 --- a/scripts/screenshots.py +++ b/scripts/screenshots.py @@ -9,18 +9,10 @@ # Load test results. import os import base64, html -from glob import glob - +import util.html_page_writer as page_writer from junitparser import JUnitXml +from glob import glob -def getbase64(src) -> str: - """Convert data in a file to base64-encoded data. - - Returns: - str. base64-encoded file data. Not prepended with data:image/... - """ - with open(src, "rb") as img_file: - return base64.b64encode(img_file.read()).decode() def get_css() -> str: """Get CSS for the page's output @@ -60,250 +52,6 @@ def get_css() -> str: } """ -def get_diff_script() -> str: - return """ - "use strict"; - - /// Get an argument given through the page's URL bar. - /// E.g. if location.href = "https://example.com/a?thing=2,key=3 - /// and [key]=thing, then this returns 2. - /// Values must be numbers or strings of characters in [a-zA-Z0-9]. - function getPageArg(key) { - let argSepPos = location.href.indexOf('?'); - if (argSepPos == -1) return null; - - // Get everything after the '?' in the URL - let argSep = location.href.substring(argSepPos + 1); - let args = argSep.split(','); - - // For each key=val - for (const arg of args) { - let parts = arg.split('='); - if (parts.length != 2) { - continue; - } - - if (parts[0] == key) { - return parts[1]; - } - } - - // No argument found - return null; - } - - // If deltaR² + deltaG² + deltaB² + deltaA² ≥ DIFF_TOLERANCE, count the pixel as - // different. - const DIFF_TOLERANCE = parseInt(getPageArg('tolerance')) || 256; - - // Non-differing pixels are at most this intense. - const DIFF_BACKGROUND_MAX_INTENSITY = 70; - - // Differing pixels are at least this intense. - const DIFF_FOREGROUND_MIN_INTENSITY = 140; - - /// Display the diff between [submittedImg] and [solutionImg] in [canvasElem]. Describe it - /// by setting the contents of [descriptionElem]. - async function generateDiff(submittedImg, solutionImg, canvasElem, descriptionElem) { - /// Returns a promise that resolves when [img] has loaded. - const waitForLoad = (img) => { - return new Promise((resolve, reject) => { - if (img.complete) resolve(); - - let onload, onerror, cleanup; - onload = () => { - cleanup(); - resolve(img); - }; - - onerror = (e) => { - cleanup(); - reject(e); - }; - - cleanup = () => { - img.removeEventListener('load', onload); - img.removeEventListener('error', onerror); - }; - - img.addEventListener('load', onload); - img.addEventListener('error', onerror); - }); - }; - - /// Log information about the diff (user-visible). - const addDiffInfo = (message) => { - const messageElem = document.createElement('div'); - messageElem.appendChild(document.createTextNode(message)); - messageElem.classList.add('diff_info'); - descriptionElem.appendChild(messageElem); - }; - - const compareImageData = (outputData, givenData, trueData) => { - const givenWidth = givenData.width; - const desiredWidth = trueData.width; - const givenHeight = givenData.height; - const desiredHeight = trueData.height; - const outputWidth = outputData.width; - let diffCount = 0; - if (givenData.width != trueData.width) { - addDiffInfo("⚠ Ignoring extra pixels in the count of differing pixels! ⚠"); - } - - outputData = outputData.data; - givenData = givenData.data; - trueData = trueData.data; - - const getPixel = (x, y, data, width) => { - // Each pixel has four components: R, G, B, A - let startIdx = x * 4 + y * width * 4; - return [ data[startIdx], data[startIdx + 1], data[startIdx + 2], data[startIdx + 3] ]; - }; - - const writePixel = (x, y, r, g, b, a) => { - const outputIdx = x * 4 + y * outputWidth * 4; - outputData[outputIdx] = r; - outputData[outputIdx + 1] = g; - outputData[outputIdx + 2] = b; - outputData[outputIdx + 3] = a; - }; - - for (let x = 0; x < Math.min(givenWidth, desiredWidth); x++) { - for (let y = 0; y < Math.min(givenHeight, desiredHeight); y++) { - const given = getPixel(x, y, givenData, givenWidth); - const desired = getPixel(x, y, trueData, desiredWidth); - const diff = [ - given[0] - desired[0], - given[1] - desired[1], - given[2] - desired[2], - given[3] - desired[3], - ]; - - // https://en.wikipedia.org/wiki/Color_difference - const diffSquared = diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2] + diff[3] * diff[3]; - if (diffSquared > DIFF_TOLERANCE) { - diffCount ++; - - /// Get the resultant value for the [idx]th component - /// of the output color. - const getPxValue = (idx) => { - // √(x) is monotonically increasing and √(0) = 0, - // and √(x) makes small increases near 0 have a - // bigger effect on its output than small increases - // far from zero. - let val = Math.sqrt(Math.abs(diff[idx])) / Math.sqrt(255); - - return val * (255 - DIFF_FOREGROUND_MIN_INTENSITY) + DIFF_FOREGROUND_MIN_INTENSITY; - }; - - const r = getPxValue(0); - const g = getPxValue(1); - const b = getPxValue(2); - - writePixel(x, y, r, g, b, 255); - } - else { - const r = given[0] / 255 * DIFF_BACKGROUND_MAX_INTENSITY; - const g = given[1] / 255 * DIFF_BACKGROUND_MAX_INTENSITY; - const b = given[2] / 255 * DIFF_BACKGROUND_MAX_INTENSITY; - writePixel(x, y, r, g, b, 255); - } - } - } - - return diffCount; - }; - - const loadingElem = document.createElement('div'); - loadingElem.innerHTML = "Generating the diff... ⏳"; - descriptionElem.replaceChildren(loadingElem); - - try { - const backgroundCanvas = document.createElement('canvas'); - let canvasDescription = ""; - - // Make sure both images have loaded before generating the diff... - await waitForLoad(submittedImg); - await waitForLoad(solutionImg); - - // Make sure the output can fit both images. - const canvasWidth = Math.max(submittedImg.width, solutionImg.width); - const canvasHeight = Math.max(submittedImg.height, solutionImg.height); - canvasElem.width = canvasWidth; - canvasElem.height = canvasHeight; - backgroundCanvas.width = canvasWidth; - backgroundCanvas.height = canvasHeight; - - const sizesDiffer = submittedImg.width != solutionImg.width || submittedImg.height != solutionImg.height; - - // Display a warning if the image sizes differ. - if (sizesDiffer) { - addDiffInfo(`⚠ The images are not the same size. ` + - `The submitted image is ${submittedImg.width}x${submittedImg.height}, ` + - `while the solution image is ${solutionImg.width}x${solutionImg.height}. ⚠`); - canvasDescription += `Submission and solution have different sizes. ` + - `Submission is ${submittedImg.width} by ${submittedImg.height} ` + - `while the solution is ${solutionImg.width} by ${solutionImg.height}.`; - } - - const ctx = canvasElem.getContext('2d'); - const backgroundCtx = backgroundCanvas.getContext('2d'); - - ctx.drawImage(submittedImg, 0, 0); - backgroundCtx.drawImage(solutionImg, 0, 0); - - let submittedData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); - let solutionData = backgroundCtx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); - - let differingPixels = compareImageData(submittedData, submittedData, solutionData); - - // Display the diff. - ctx.clearRect(0, 0, canvasWidth, canvasHeight); - ctx.putImageData(submittedData, 0, 0); - - addDiffInfo(`Diff: In the two images, ${differingPixels} pixels differ.`); - canvasDescription += `${differingPixels} pixels differ between the two images. `; - - // Give the percent difference. - if (!sizesDiffer && differingPixels > 0) { - const totalPixels = canvasWidth * canvasHeight; - const percentDiff = differingPixels / totalPixels * 100; - - // Round to the nearest 10th - const roundedDiff = Math.floor(percentDiff * 10 + 0.5) / 10; - - const differMessage = `This is approximately ${roundedDiff}%.`; - - addDiffInfo(differMessage); - canvasDescription += differMessage; - } - - canvasElem.setAttribute('title', "Image Diff: " + canvasDescription); - } catch(e) { - addDiffInfo(`Failed to generate the diff: ${e}. Consider using an online comparison tool.`); - } - - loadingElem.remove(); - } - """ - -def get_html_setup(page_title) -> str: - """Get HTML that sets up the document (everything from to
, inclusive) - """ - - return """ - - - - - -