grading
Version:
Grading of student submissions, in particular programming tests.
181 lines • 10.3 kB
JavaScript
"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