UNPKG

grading

Version:

Grading of student submissions, in particular programming tests.

181 lines 10.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.cmdPrepare = void 0; const fs_1 = __importDefault(require("fs")); const cliUtil_1 = require("./cliUtil"); const cmdCheck_1 = require("./cmdCheck"); const cmdGrade_1 = require("./cmdGrade"); const cmdPDF_1 = require("./cmdPDF"); const fsUtil_1 = require("../fsUtil"); const grading_1 = require("../grade/grading"); const schemaGenerator_1 = require("../grade/schemaGenerator"); const path_1 = require("path"); // .argument('[solutionFolder]', 'Folder with solution, should match structure of a submission', '.') // .option('-r, --reportsDirSolution <reportsDir>', // 'folder for generated JUnit and coverage reports, mounted in docker as /reports', './gradingOut/solution/reports') // .addOption(optCheckDir) // .addOption(optNpmCacheDir) // .addOption(optCacheDir) // .option('-g --gradingSchemaFile <schemaFile>', 'JSON file with grading schema (*.grading.json)', './solution/exam.grading.json') // .option('-c --createGrading', 'Instead of grading, create grading scheme based on checks run on solution. This option specifies the file name. Cannot be used with -g', '') // .option('-di, --dockerImage <dockerImage>', 'Docker image name', 'node:18.10-alpine') // .option('-ds, --dockerScript <dockerScript>', 'Script to run in docker, this must be path in the vm', 'check/checkInDocker.sh') // .option('-tod, --timeoutPerDockerRun <timeoutPerDockerRun>', 'Timeout per docker run (for each submission)', '300s') // .option('-o --pdfDir <pdfDir>', 'The folder into which the PDF report is generated (using Moodle\'s folder structure).', // './gradingOut/solution/solutionReport') // .option('--latexMainFile <latexMainFile>', 'Name of LaTeX main file (importing fragment via input)', "check/latex/grading.tex") // .option('--latexFragmentFile <latexFragmentFile>', 'Name of LaTeX fragment (previously created via grade)', './gradingOut/solution/results.tex') // .option('--workDir <workDir>', 'Name of working folder, this folder is cleaned before and after)', "./gradingOut/_working") // .option('-l --latex <latex>', 'Name of LaTeX tool', 'xelatex') // .option('--latexConsole', 'Show LaTex console output', false) // .option('--validateOnly', 'Do not run tests, only validate grading schema') // .addOption(optQuiet) // .addOption(optVerbose) async function cmdPrepare(solutionFolder, options) { (0, cliUtil_1.verbosity)(options); (0, cliUtil_1.log)("Preparing grading."); try { const lecturer = { name: 'Lecturer', submissionId: 'solution' }; // part 1: run check on solution const checkDir = options.checkDir; const reportsDir = options.reportsDirSolution; const npmCacheDir = options.npmCacheDir; const cacheDir = options.cacheDir; const clipDir = options.clipDir; const schemaFile = options.gradingSchemaFile; const pdfDir = options.pdfDirSolution; const workDir = options.workDir; const validateOnly = options.validateOnly; const volumes = options.volumes; const patchFolder = options.patchFolder; const ignoredDirs = [...options.ignoredDirs, options.submissionsDir]; if (!options.dockerUserDir) { program.error("dockerUserDir must be set."); } if (!validateOnly) { await (0, fsUtil_1.ensureFolderExists)(solutionFolder, "Check solution folder."); await (0, fsUtil_1.ensureFolderExists)(checkDir, "Check check folder."); if (npmCacheDir && !await (0, fsUtil_1.folderExists)(npmCacheDir, 'npmCacheDir')) await (0, fsUtil_1.createDir)(npmCacheDir); if (cacheDir && !await (0, fsUtil_1.folderExists)(cacheDir, 'cacheDir')) await (0, fsUtil_1.createDir)(cacheDir); if (clipDir != "" && !await (0, fsUtil_1.folderExists)(clipDir, 'cacheDir')) await (0, fsUtil_1.createDir)(clipDir); } if (!validateOnly && !options.skipDocker) { if (await (0, fsUtil_1.folderExists)(reportsDir, 'reportsDirSolution')) await (0, fsUtil_1.rmDir)(reportsDir); await (0, fsUtil_1.createDir)(reportsDir); } const createGradingFile = (options.createGradingFile && options.createGradingFile.length > 0) ? options.createGradingFile : null; if (!createGradingFile) { await (0, fsUtil_1.ensureFileExists)(schemaFile, "Check grading schema file."); } else { if (validateOnly) { program.error("Validating a newly created grading file does not make any sense."); } } let projectDir = "."; try { projectDir = await (0, fsUtil_1.findDirContainingFile)(solutionFolder, options.projectFile, ignoredDirs); if (!validateOnly && !options.skipDocker) { (0, cliUtil_1.verb)("Project dir: " + projectDir); } else { (0, cliUtil_1.log)("Project dir: " + projectDir); } } catch (err) { (0, cliUtil_1.warn)(`Project file ${options.projectFile} not found in solution folder "${solutionFolder}", use "${projectDir}" (i.e. ${(0, path_1.resolve)(projectDir)}).`); } if (!validateOnly && !options.skipDocker) { const clipUserDir = await (0, cmdCheck_1.createClipSubmissionDir)(clipDir, lecturer.submissionId); await (0, cmdCheck_1.runPreCheckScript)(options.preCheckScript, projectDir, clipUserDir, checkDir, true, options); try { await (0, cmdCheck_1.runDocker)("prepare", false, checkDir, projectDir, reportsDir, npmCacheDir, cacheDir, clipDir, volumes, lecturer.submissionId, { dry: options.dry, dockerImage: options.dockerImage, dockerUserDir: options.dockerUserDir, dockerWorkDir: options.dockerWorkDir, dockerShellCmd: options.dockerShellCmd, dockerScript: options.dockerScript, dockerArgs: options.dockerArgs, timeoutPerDockerRun: options.timeoutPerDockerRun, preserveContainer: options.preserveContainer }, options); } finally { await (0, cmdCheck_1.runPostCheckScript)(options.postCheckScript, projectDir, clipUserDir, checkDir, true, options); if (clipUserDir) { await (0, cmdCheck_1.rmClipSubmissionDir)(clipUserDir, lecturer.submissionId); } } } else if (options.skipDocker) { (0, cliUtil_1.log)("Skipping Docker run."); } // part 2a: create grading scheme if (createGradingFile) { (0, cliUtil_1.log)(`Generating grading schema from check results (submission id ${lecturer.submissionId}).`); const gradingSchema = await (0, schemaGenerator_1.generateGradingSchema)(reportsDir, lecturer); await fs_1.default.promises.writeFile(createGradingFile, JSON.stringify(gradingSchema, null, 4), { encoding: "utf-8" }); (0, cliUtil_1.log)(`Initial grading schema written to ${createGradingFile}`); } else { // part 2b: grade solution const gradingSchema = await (0, cliUtil_1.parseJsonWithComments)(schemaFile); if (validateOnly) { if (!(0, grading_1.validate)(gradingSchema)) { program.error("There were validation errors."); } else { (0, cliUtil_1.log)(`${SEP}\nDone.`); return; // ok } } let results = await (0, grading_1.doGrading)({ gradingSchema, manualCorrection: undefined, submitters: [lecturer], reportsDir, startMarker: options.testOutputStartMarker, endMarker: options.testOutputEndMarker, patchFolder }); if (!options.latexFragmentFileSolution) { program.error("Missing option --latexFragmentFileSolution"); } await (0, cmdGrade_1.createLatexFragment)(results, { latexFragmentFile: options.latexFragmentFileSolution, dry: options.dry, workDir: workDir, reportsDir: reportsDir, patchFolder: patchFolder }); // part 3: create PDF if (!options.skipPDF) { const latex = options.latex; const latexMainFile = options.latexMainFile; await (0, fsUtil_1.ensureFileExists)(latexMainFile, "Check option --latexMainFile."); const latexFragmentFile = options.latexFragmentFileSolution; const latexMainFolderCopy = options.latexMainFolderCopy; await (0, fsUtil_1.ensureFileExists)(latexFragmentFile, "Check option --latexFragmentFileSolution."); if (await (0, fsUtil_1.folderExists)(pdfDir, 'pdfDirSolution')) await (0, fsUtil_1.rmDir)(pdfDir); if (await (0, fsUtil_1.folderExists)(workDir, 'workDir')) await (0, fsUtil_1.rmDir)(workDir); await (0, fsUtil_1.createDir)(pdfDir); await (0, fsUtil_1.createDir)(workDir); const { latexMainLatex, latexMainPDF, latexMainWOExt } = (0, cmdPDF_1.prepareLatexFiles)(latexMainFile, workDir, latexFragmentFile, latexMainFolderCopy); await (0, cmdPDF_1.runLatex)((0, cliUtil_1.createSubmitterDir)(lecturer), latexMainLatex, pdfDir, latex, workDir, latexMainPDF, latexMainWOExt, options.latexConsole, options); } else { (0, cliUtil_1.log)("Skipping PDF generation."); } } } catch (err) { (0, cliUtil_1.error)(`${SEP}`); if (err instanceof Error) { (0, cliUtil_1.verb)(`${err.stack}`); } // error(`${SEP}\nError: ${err}`); program.error(String(err)); } (0, cliUtil_1.log)(`${SEP}\nDone.`); } exports.cmdPrepare = cmdPrepare; //# sourceMappingURL=cmdPrepare.js.map