export function processData () { const folderInput = document.querySelector("#folder"); const header = [ "submissionid", "file", "filesize", "timestamp", "humandate", "grade", "userid", "exerciseid", ]; function generateSubCode (algorithmInIlm) { window.program_obj.functions = JSON.parse(algorithmInIlm).functions; window.program_obj.globals = JSON.parse(algorithmInIlm).globals; return window.ivprogCore.generateCode(); } function prepareData (map, submission) { if (!submission) { return map; } const exercID = submission["exerciseid"]; const studentID = submission["userid"]; let exercMap = null; if (map.has(exercID)) { exercMap = map.get(exercID); } else { exercMap = new Map(); map.set(exercID, exercMap); } if (exercMap.has(studentID)) { exercMap.get(studentID).push(submission); } else { exercMap.set(studentID, [submission]); } return map; } folderInput.addEventListener("change", async () => { const files = Array.from(folderInput.files); const idx = files.find((f) => f.name == "index.csv"); const folderName = idx.webkitRelativePath.replace(idx.name, ""); console.debug(idx); console.debug(folderName); const data = await idx.text(); console.debug(data); const csvEntries = data .split("\n") .slice(1) .filter((line) => line.length > 0) .map((line) => line.split(",")) .map((vec) => { const obj = {}; vec.forEach((val, i) => (obj[header[i]] = val)); return obj; }); console.debug(csvEntries); const blockExercMap = csvEntries.reduce(prepareData, new Map()); //console.log(Array.from(blockExercMatrix.entries())); const getFilePath = async function (submission) { const path = `${folderName}${submission["file"]}`; const file = files.find((f) => f.webkitRelativePath == path); const text = await file.text(); return text; }; const matrix = {}; let counter = 0; blockExercMap.forEach((studentsMap, exercID) => { const column = []; studentsMap.forEach(async (submissions, studentID) => { submissions = submissions.sort((a, b) => { return parseInt(a["timestamp"]) - parseInt(b["timestamp"]); }); counter++; submissions.forEach(async (submission, index, array) => { counter++; const student = {}; student["grade"] = Math.max(0, parseFloat(submission["grade"])); student["timestamp"] = parseInt(submission["timestamp"]); student["student_id"] = studentID; student["TES"] = 0; student["DES"] = 0; student["D/T"] = 0; let previousCode = ""; if (index > 0) { student["TES"] = parseInt(submission["timestamp"]) - parseInt(array[index - 1]["timestamp"]); const previousFile = await getFilePath(array[index - 1]); const previous = window.ivprogCore .prepareActivityToStudentHelper(previousFile) .getOrElse(1); if (previous == 1) { console.error( `A submission from ${studentID} to ${exercID} is invalid` ); return; } previousCode = generateSubCode(previous.algorithmInIlm); } const currentFile = await getFilePath(submission); const current = window.ivprogCore .prepareActivityToStudentHelper(currentFile) .getOrElse(2); if (current == 2) { console.error( `A submission from ${studentID} to ${exercID} is invalid` ); return; } const currentCode = generateSubCode(current.algorithmInIlm); if (previousCode === "") { const logs = JSON.parse(currentFile.split("::logs::")[1]); const event = logs[0]; if (event == null) { // empty submission student["TES"] = 0; } else if (event.length === 4) { student["TES"] = parseInt(submission["timestamp"]) - Math.floor(parseInt(event[2]) / 1000); } else { student["TES"] = parseInt(submission["timestamp"]) - Math.floor(parseInt(event[1]) / 1000); } } student["DES"] = window.ivprogCore.levenshteinDistance( previousCode, currentCode ); const ratio = student["TES"] === 0 ? 0 : student["DES"] / student["TES"]; student["D/T"] = isNaN(ratio) ? 0 : ratio; column.push(student); counter--; }); counter--; }); matrix[exercID] = column; }); function download (file, text) { //creating an invisible element const element = document.createElement("a"); element.setAttribute( "href", "data:text/plain," + encodeURIComponent(text) ); element.setAttribute("download", file); element.innerHTML = file; element.classList.add("ui", "primary", "button"); // Above code is equivalent to // document.querySelector("#downloads").appendChild(element); } function getExercHeader (id) { const props = ["TES", "DES", "grade", "D/T", "timestamp"]; return props.reduce((acc, prop) => acc + `${id}_${prop},`, ""); } const id = setInterval(() => { if (counter == 0) { clearInterval(id); for (const exercID in matrix) { let csv = ""; let firstLine = "student_id,"; firstLine += getExercHeader(exercID); for (const submission of matrix[exercID]) { csv += `${submission["student_id"]},`; csv += `${submission["TES"]},`; csv += `${submission["DES"]},`; csv += `${submission["grade"]},`; csv += `${submission["D/T"]},`; csv += `${submission["timestamp"]}`; csv += "\n"; } download(`${exercID}.csv`, `${firstLine}\n${csv}`); } } }, 1000); }); }