UNPKG

grading

Version:

Grading of student submissions, in particular programming tests.

161 lines 8.67 kB
"use strict"; 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