UNPKG

codegradx

Version:
276 lines (255 loc) 10.1 kB
// batch.mjs // Time-stamp: "2021-09-15 17:51:25 queinnec" import { CodeGradX as cx } from '../codegradx.mjs'; /** Re-export the `CodeGradX` object */ export const CodeGradX = cx; export default CodeGradX; import { parsexml } from './parsexml.mjs'; /** Send a batch of files that is, multiple answers to be marked against an Exercise. That file is selected with an input:file widget in the browser. @param {DOMform} form - the input:file widget @param {String} currentFileName - file name @returns {Promise<Batch>} yielding a Batch. The form DOM element must contain an <input type='file' name='content'> element. This code only runs in a browser providing the FormData class. */ CodeGradX.Exercise.prototype.sendBatchFromDOM = function (form, currentFileName) { const exercise = this; const state = CodeGradX.getCurrentState(); currentFileName = currentFileName || state.currentFileName; state.debug('sendBatchFile1', currentFileName); if ( ! exercise.safecookie ) { return Promise.reject("Non deployed exercise " + exercise.name); } function processResponse (response) { //console.log(response); state.debug('sendBatchFile2', response); return parsexml(response.entity).then(function (js) { //console.log(js); state.debug('sendBatchFile3', js); js = js.fw4ex.multiJobSubmittedReport; exercise.uuid = js.exercise.$.exerciseid; const batch = new CodeGradX.Batch({ exercise: exercise, responseXML: response.entity, response: js, personid: CodeGradX._str2num(js.person.$.personid), archived: CodeGradX._str2Date(js.batch.$.archived), batchid: js.batch.$.batchid, pathdir: js.$.location, finishedjobs: 0 }); state.debug('sendBatchFile6', batch.batchid); return Promise.resolve(batch); }); } const basefilename = currentFileName.replace(new RegExp("^.*/"), ''); const headers = { // Useless since we post a FormData: //"Content-Type": "multipart/form-data", "Content-Disposition": ("inline; filename=" + basefilename), "Accept": 'application/json' }; const fd = new FormData(form); return state.sendAXServer('a', { path: ('/exercise/' + exercise.safecookie + '/batch'), method: "POST", headers: headers, entity: fd }).then(processResponse); }; /** submit a new Batch and return it as soon as submitted successfully. This variant sends the content as a file. @param {ByteString} tgz @param {String} filename @returns {Promise<Batch>} yielding Batch */ CodeGradX.Exercise.prototype.sendBatch = function (tgz, filename) { const exercise = this; const state = CodeGradX.getCurrentState(); state.debug('sendBatchFile1', filename); if ( ! exercise.safecookie ) { return Promise.reject("Non deployed exercise " + exercise.name); } function processResponse (response) { //console.log(response); state.debug('sendBatchFile3', response); return parsexml(response.entity).then(function (js) { //console.log(js); state.debug('sendBatchFile4', js); js = js.fw4ex.multiJobSubmittedReport; exercise.uuid = js.exercise.$.exerciseid; const batch = new CodeGradX.Batch({ exercise: exercise, responseXML: response.entity, response: js, personid: CodeGradX._str2num(js.person.$.personid), archived: CodeGradX._str2Date(js.batch.$.archived), batchid: js.batch.$.batchid, pathdir: js.$.location, finishedjobs: 0 }); state.debug('sendBatchFile5', batch.batchid); return Promise.resolve(batch); }); } const basefilename = filename.replace(new RegExp("^.*/"), ''); const headers = { "Content-Type": "application/octet-stream", "Content-Disposition": `inline; filename=${basefilename}`, "Accept": 'application/json' }; return state.sendAXServer('a', { path: ('/exercise/' + exercise.safecookie + '/batch'), method: "POST", headers: headers, entity: tgz }).then(processResponse); }; /** Get the current state of the Batch report that is, always fetch it. See also `getFinalReport()` to get the final report of the batch where all answers are marked. @param {Object} parameters - parameters {@see sendRepeatedlyESServer} @returns {Promise<Batch>} yielding Batch */ CodeGradX.Batch.prototype.getReport = function (parameters) { const batch = this; const state = CodeGradX.getCurrentState(); state.debug('getBatchReport1', batch); parameters = Object.assign({ // So progress() may look at the current version of the batch report: batch: batch }, CodeGradX.Batch.prototype.getReport.default, parameters); const path = batch.getReportURL(); function processResponse (response) { //console.log(response); state.debug('getBatchReport2', response, batch); batch.originServer = response.url.replace(/^(.*)\/s\/.*$/, "$1"); function processJS (js) { //console.log(js); state.debug('getBatchReport3', js); js = js.fw4ex.multiJobStudentReport; batch.batchid = js.$.batchid; batch.label = js.$.label; batch.pathdir = js.$.pathdir; batch.totaljobs = CodeGradX._str2num(js.$.totaljobs); batch.finishedjobs = CodeGradX._str2num(js.$.finishedjobs); batch.archived = CodeGradX._str2Date(js.$.archived); batch.exercise = js.exercise; batch.exercise.uuid = js.exercise.$.exerciseid; batch.exercise.exerciseid = js.exercise.$.exerciseid; batch.exercise.totalMark = js.exercise.$.totalMark; delete batch.exercise.$; batch.jobs = Object.create(null); //console.log(js); function processJob (jsjob) { //console.log(jsjob); let job = new CodeGradX.Job({ exercise: batch.exercise, XMLjob: jsjob, jobid: jsjob.$.jobid, pathdir: jsjob.$.location, label: jsjob.$.label, problem: false, mark: CodeGradX._str2num2decimals( jsjob.marking.$.mark), totalMark: CodeGradX._str2num2decimals( jsjob.marking.$.totalMark), started: CodeGradX._str2Date(jsjob.marking.$.started), finished: CodeGradX._str2Date(jsjob.marking.$.finished) }); if ( jsjob.$.problem && jsjob.$.problem !== '0' ) { job.problem = true; } job.duration = CodeGradX.computeDuration( job.finished, job.started); batch.jobs[job.label] = job; return job; } if ( js.jobStudentReport ) { if ( Array.isArray(js.jobStudentReport) ) { js.jobStudentReport.forEach(processJob); } else { processJob(js.jobStudentReport); } } return Promise.resolve(batch); } batch.XMLreport = response.entity; return parsexml(response.entity) .then(processJS) .catch(function (reason) { /* eslint "no-console": 0 */ console.log(reason); console.log(response); return Promise.reject(reason); }); } return state.sendRepeatedlyESServer('s', parameters, { path: path, method: 'GET', headers: { "Accept": "application/json" } }).then(processResponse); }; CodeGradX.Batch.prototype.getReport.default = { step: 5, // seconds attempts: 100, progress: function (/*parameters*/) {} }; /** Get the final state of the Batch report where all answers are marked. This method will update the `finishedjobs` and `jobs` fields. @param {Object} parameters - parameters {@see sendRepeatedlyESServer} @returns {Promise<Batch>} yielding Batch */ CodeGradX.Batch.prototype.getFinalReport = function (parameters) { const batch = this; const state = CodeGradX.getCurrentState(); state.debug('getBatchFinalReport1', batch); if ( batch.finishedjobs && batch.finishedjobs === batch.totaljobs ) { // Only return a complete report return Promise.resolve(batch); } parameters = Object.assign({ // So progress() may look at the current version of the batch report: batch: batch }, CodeGradX.Batch.prototype.getReport.default, parameters); if ( parameters.step < CodeGradX.Batch.prototype.getReport.default.step ) { parameters.step = CodeGradX.Batch.prototype.getReport.default.step; } function tryFetching () { state.debug('getBatchFinalReport3', parameters); // Get at least one report to access finishedjobs and totaljobs: return batch.getReport(parameters).then(fetchAgainReport); } function fetchAgainReport () { state.debug('getBatchFinalReport2', batch); if ( batch.finishedjobs < batch.totaljobs ) { const dt = parameters.step * 1000; // seconds return Promise.resolve(batch).delay(dt).then(tryFetching); } else { return Promise.resolve(batch); } } return tryFetching(); }; CodeGradX.Batch.prototype.getReportURL = function () { const batch = this; if ( ! batch.pathdir ) { batch.batchid = CodeGradX.normalizeUUID(batch.batchid); batch.pathdir = '/b' + batch.batchid.replace(/-/g, '').replace(/(.)/g, "/$1"); } const path = batch.pathdir + '/' + batch.batchid + '.xml'; return path; }; // end of batch.mjs