grading
Version:
Grading of student submissions, in particular programming tests.
161 lines • 8.67 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.prepareLatexFiles = exports.runLatex = exports.cmdPDF = void 0;
const cliUtil_1 = require("./cliUtil");
const fsUtil_1 = require("../fsUtil");
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
// .description('Create PDF reports using LaTeX based on results previously created via grade.')
// .argument('<pdfDir>', 'The folder into which the PDF reports are generated (using Moodle\'s folder structure)')
// .addOption(optSubmissionsDir)
// .option('-p --pdfZipFile <pdfZipFile>', 'File name of created zip file to be uploaded to Moodle (Alle Abgaben Anzeigen/Bewertungsvorgang: Mehrere Feedbackdateien in einer Zip-Datei hochladen); with variables in ${..} (examName, date, time).', "${moodleFile}_graded_${date}-${time}.csv")
// .option('--latexMainFile <latexMainFile>', 'Name of LaTeX main file (importing fragment via input)', "check/latex/grading.tex")
// .option('--latexFragment <latexFragmentFile>', 'Name of LaTeX fragment (previously created via grade)', "reports/results.tex")
// .option('--workDir <workDir>', 'Name of working folder, this folder is cleaned before and after)', "_working")
// .option('-l --latex <latex>', 'Name of LaTeX tool', 'xelatex')
// .option('-c --clean', 'Automatically clean folder with PDFs', false)
// .addOption(optQuiet)
// .addOption(optVerbose)
async function cmdPDF(options, runInternally = false) {
(0, cliUtil_1.verbosity)(options);
try {
const submissionsDir = options.submissionsDir;
await (0, fsUtil_1.ensureFolderExists)(submissionsDir, "Check submissions folder.");
const latexMainFile = options.latexMainFile;
const latexMainFolderCopy = options.latexMainFolderCopy;
await (0, fsUtil_1.ensureFileExists)(latexMainFile, "Check latex main file.");
const latexFragmentFile = options.latexFragmentFile;
await (0, fsUtil_1.ensureFileExists)(latexFragmentFile, "Check latex fragment file.");
const selected = (0, cliUtil_1.retrieveSelectedFilter)(options);
const selectPatched = options.selectPatched;
const patchedSubmissions = [];
const patchFolder = options.patchFolder;
const clean = selected.length === 0;
const pdfDir = options.pdfDir;
const workDir = options.workDir;
if (clean && await (0, fsUtil_1.folderExists)(pdfDir, 'pdfDir'))
await (0, fsUtil_1.rmDir)(pdfDir); // do not clean if only one pdf is regenerated
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 } = prepareLatexFiles(latexMainFile, workDir, latexFragmentFile, latexMainFolderCopy);
const submissionDirs = await (0, fsUtil_1.readDir)(submissionsDir, 'dir', false, 'Check submissions folder.');
//const submitters = submissionDirs.map(subDir => parseSubmitter(subDir));
if (selectPatched) {
if (selected.length > 0) {
(0, cliUtil_1.warn)(`--selectPatched and --selected are mutually exclusive, ignoring --selectPatched.`);
}
else {
const patchFiles = await (0, fsUtil_1.readDir)(patchFolder, 'file', false, 'Check patch folder.');
patchedSubmissions.push(...patchFiles.map(f => {
const n_s = f.substring(0, f.lastIndexOf('.')).split('_');
return { name: n_s[0], submissionId: n_s[1] };
}));
(0, cliUtil_1.verb)(`Found ${patchFiles.length} patch files: ${patchedSubmissions.map(s => s.name).join(', ')}.`);
}
}
let count = 0;
let max = Number.parseInt(options.max);
const latex = options.latex;
for (const subDir of submissionDirs) {
if (max > 0 && count >= max) {
(0, cliUtil_1.log)(`Maximal submissions reached, skipping ${submissionDirs.length - count} submissions.`);
break;
}
const { name, submissionId: submissionId } = (0, cliUtil_1.parseSubmitter)(subDir);
if (!(0, cliUtil_1.isSubmissionSelected)(selected, patchedSubmissions, name, submissionId)) {
(0, cliUtil_1.verb)(`Skip user ${name}, id ${submissionId}`);
continue;
}
count++;
await runLatex(subDir, latexMainLatex, pdfDir, latex, workDir, latexMainPDF, latexMainWOExt, options.latexConsole, options);
}
(0, cliUtil_1.log)(`Created ${count} PDF reports`);
if (!options.noPDFZip) {
const fileName = (0, cliUtil_1.generateFileName)(options.pdfZipFile, { dateTime: Date.now() });
await (0, fsUtil_1.zip)(pdfDir, fileName);
(0, cliUtil_1.log)(`Created zip file to upload ${fileName}`);
if (options.clean) {
await (0, fsUtil_1.rmDir)(pdfDir);
}
}
}
catch (err) {
(0, cliUtil_1.error)(`${SEP}\nError: ${err}`);
program.error(String(err));
}
if (!runInternally) {
(0, cliUtil_1.log)(`${SEP}\nDone`);
}
}
exports.cmdPDF = cmdPDF;
/**
*
* @param subDir directory to which to write to inside pdfDir
* @param latexMainLatex
* @param pdfDir the directory where the pdfs are written to
* @param latex
* @param workDir
* @param latexMainPDF
* @param latexMainWOExt
* @param latexConsole
* @param options
*/
async function runLatex(subDir, latexMainLatex, pdfDir, latex, workDir, latexMainPDF, latexMainWOExt, latexConsole, options) {
const submitter = (0, cliUtil_1.parseSubmitter)(subDir);
const latexSnippet = `\\def\\submissionid{${submitter.submissionId}}\\input{${latexMainLatex}}`;
const subPDFDir = path_1.default.join(pdfDir, subDir);
await (0, fsUtil_1.createDir)(subPDFDir);
const args = ['-halt-on-error'];
if (!latexConsole) {
args.push('-interaction=batchmode');
}
args.push(latexSnippet);
(0, cliUtil_1.log)(`Create PDFs: ${submitter.name}: ${latex} ${args.join(' ')}`);
await (0, fsUtil_1.execShell)(latex, args, { ...options, colored: options.colored || false }, { indent: true, prefix: '>', cwd: workDir });
const generatedPDF = path_1.default.join(workDir, latexMainPDF);
if (await (0, fsUtil_1.fileExists)(generatedPDF)) {
const targetPDF = path_1.default.join(subPDFDir, latexMainWOExt + "-" + submitter.submissionId + ".pdf");
if (await (0, fsUtil_1.fileExists)(targetPDF)) {
await (0, fsUtil_1.rmFile)(targetPDF);
}
(0, fs_1.renameSync)(generatedPDF, targetPDF);
}
else {
program.error(`${generatedPDF} not generated, run with --latexConsole to show LaTeX output.`);
}
}
exports.runLatex = runLatex;
function prepareLatexFiles(latexMainFile, workDir, latexFragmentFile, latexMainFolderCopy) {
const latexMainLatex = path_1.default.basename(latexMainFile);
const latexMainWOExt = latexMainLatex.substring(0, latexMainLatex.length - path_1.default.extname(latexMainLatex).length);
const latexMainPDF = latexMainWOExt + ".pdf";
if (!(0, fsUtil_1.folderExists)(workDir)) {
(0, fsUtil_1.createDir)(workDir);
}
if (latexMainFolderCopy) {
const latexMainFolder = path_1.default.dirname(latexMainFile);
(0, fs_1.readdirSync)(latexMainFolder, { withFileTypes: true }).forEach(dirent => {
const ext = path_1.default.extname(dirent.name);
if (dirent.isFile() && !["aux", "bbl", "blg", "log", "out", "gz", "thm", "toc", "json"].includes(ext)) {
const srcFile = path_1.default.join(latexMainFolder, dirent.name);
const targetFile = path_1.default.join(workDir, dirent.name);
if ((0, fs_1.existsSync)(targetFile)) {
(0, fs_1.rmSync)(targetFile);
}
(0, fs_1.copyFileSync)(srcFile, targetFile);
}
});
}
else {
(0, fs_1.copyFileSync)(latexMainFile, path_1.default.join(workDir, latexMainLatex));
}
(0, fs_1.copyFileSync)(latexFragmentFile, path_1.default.join(workDir, path_1.default.basename(latexFragmentFile)));
return { latexMainLatex, latexMainPDF, latexMainWOExt };
}
exports.prepareLatexFiles = prepareLatexFiles;
//# sourceMappingURL=cmdPDF.js.map
;