UNPKG

28.3 kBJavaScriptView Raw
1#!/usr/bin/env node
2/**
3
4 Command line utilities to interact with the CodeGradX infrastructure
5
6## Installation
7
8```bash
9npm install codegradxagent
10```
11
12## Usage
13
14This script is a command line utility to interact with the CodeGradX
15infrastructure. It runs on Node.js.
16
17@module codegradxagent
18@author Christian Queinnec <Christian.Queinnec@codegradx.org>
19@license MIT
20@see {@link http://codegradx.org/|CodeGradX} site.
21
22 */
23
24var getopt = require('node-getopt');
25var fs = require('fs');
26var when = require('when');
27var nodefn = require('when/node');
28var _ = require('lodash');
29var CodeGradX = require('codegradxlib');
30
31// Exports what CodeGradX exports:
32module.exports = CodeGradX;
33
34CodeGradX.Agent = function (initializer) {
35 this.state = new CodeGradX.State();
36 this.configuration = [
37 ['h', 'help', "display that help"],
38 ['v', 'verbose', "show what the agent is doing"],
39 ['V', 'Verbose', "be more verbose"],
40 ['', 'user=[LOGIN]', "User's login"],
41 ['', 'password=[PASSWORD]', "User's password"],
42 ['', 'credentials=[FILE]', "a JSON file containing credentials"],
43 ['', 'update-credentials', "Update JSON file holding credentials"],
44 ['', 'stuff=[FILE]', "the file to submit"],
45 ['', 'follow', "fetch the derived reports"],
46 ['d', 'xmldir', "directory where to store reports"],
47 ['c', 'counter=[NUMBER]', "start value when counting reports"],
48 ['t', 'type=[TYPE]', "type of submission"],
49 ['e', 'exercise=[SAFECOOKIE]', "identifier of an exercise"],
50 ['r', 'resume=[FILE]', "resume job, exercise or batch"],
51 ['', 'retry=[NUMBER]', "number of attempts"],
52 ['', 'offset=[NUMBER]', "wait time before attempting"],
53 ['', 'timeout=[NUMBER]', "wait time between attempts"]
54 ];
55 if ( _.isFunction(initializer) ) {
56 this.configuration = initializer(this.configuration);
57 }
58 //console.log(this.parser);
59 // .bindHelp() forces the process to exit after displaying help!
60 this.credentialsFile = './.fw4ex.json';
61 this.startTime = _.now();
62 this.xmldir = '.';
63 this.counter = 0;
64 var agent = this;
65 // Customize
66 if ( _.isFunction(initializer) ) {
67 agent = initializer.call(agent, agent);
68 }
69 this.parser = getopt.create(this.configuration);
70 this.parser.errorFunc = function (e) {
71 throw e;
72 };
73 CodeGradX.getCurrentAgent = function () {
74 return agent;
75 };
76};
77
78/** Get the current agent (if defined)
79
80 @returns {Agent}
81 @property {object} Agent.commands
82 @property {Hashtable} Agent.commands.options - option name is the key
83 @property {object} Agent.commands.argv - remaining files
84
85*/
86
87CodeGradX.getCurrentAgent = function () {
88 throw new Error("no Agent");
89};
90
91/** Display a short summary of this script.
92*/
93
94CodeGradX.Agent.prototype.usage = function () {
95 var agent = this;
96 agent.parser.showHelp();
97};
98
99/** Promisify writing to a file.
100 This code is similar to CodeGradX.readFileContent.
101
102 @param {string} filename - file to write
103 @param {string} data - new content of the file
104 @returns {Promise<null>} yields control
105
106*/
107
108CodeGradX.writeFileContent = function (filename, data) {
109 return nodefn.call(fs.writeFile, filename, data);
110};
111
112/** Debug utility to visualize what the agent is doing.
113 It uses `console.log` for that visualization.
114
115 @param {object} arguments
116 @returns {nothing}
117*/
118
119CodeGradX.Agent.prototype.debug = function () {
120 var agent = this;
121 if ( ! agent.verbose ) {
122 return;
123 }
124 var t = _.now() - agent.startTime;
125 // Separate seconds from milliseconds:
126 var msg = ('000'+t).replace(/(...)$/, ".$1") + ' ';
127 msg = msg.replace(/^0*/, '');
128 for (var i=0 ; i<arguments.length ; i++) {
129 if ( arguments[i] === null ) {
130 msg += 'null ';
131 } else if ( arguments[i] === undefined ) {
132 msg += 'undefined ';
133 } else {
134 msg += arguments[i].toString() + ' ';
135 }
136 }
137 if ( agent.verbose > 1 ) {
138 agent.state.log.showAndRemove();
139 }
140 console.log(msg);
141};
142
143/** Parse options then run the agent.
144
145 @param {Array<string>} strings.
146 @returns {Promise<???>} depending on option `type`
147
148*/
149
150CodeGradX.Agent.prototype.process = function (strings) {
151 var agent = this;
152 return agent.parseOptions(strings).run();
153};
154
155/** Run the agent.
156
157 @returns {Promise<???>} depending on option `type`
158
159*/
160
161CodeGradX.Agent.prototype.run = function () {
162 var agent = this;
163
164 if ( ! agent.commands ) {
165 return when.reject("Nothing to do!");
166 }
167
168 function agentProcess () {
169 return agent.processAuthentication()
170 .then(_.bind(agent.processType, agent));
171 }
172
173 if ( agent.commands.options &&
174 agent.commands.options.offset &&
175 agent.commands.options.offset > 0 ) {
176 var dt = agent.commands.options.offset * 1000;
177 agent.commands.options.offset = 0;
178 return when(agent).delay(dt).then(agentProcess);
179 }
180
181 return agentProcess();
182};
183
184/** Parse options.
185
186 @param {Array<string>} strings
187 @return {Agent}
188 @throw {parseOptionsException}
189*/
190
191CodeGradX.Agent.prototype.parseOptions = function (strings) {
192 var agent = this;
193 var commands = {};
194 try {
195 commands = agent.getOptions(strings);
196 agent.commands = commands;
197 } catch (exc) {
198 commands.length = 0;
199 }
200 //console.log(strings, commands);//DEBUG
201
202 if ( commands.length === 0 || ! commands.options ) {
203 commands.options = {help: 1};
204 }
205
206 // Process global options:
207 if ( commands.options.verbose ) {
208 agent.verbose = 1;
209 }
210 if ( commands.options.Verbose ) {
211 // Be more verbose
212 agent.verbose = 2;
213 }
214 if ( commands.options.help ) {
215 agent.usage();
216 return agent;
217 }
218
219 if ( commands.options.xmldir ) {
220 agent.xmldir = commands.options.xmldir;
221 }
222 if ( commands.options.counter ) {
223 agent.counter = commands.options.counter;
224 }
225
226 if ( commands.options.resume ) {
227 // Default type for resumptions:
228 agent.commands.options.type = 'resume';
229 }
230 return agent;
231};
232
233/** Handle authentication, read and/or update credentials.
234 By default the credentials file is name `./fw4ex.json`.
235
236 @returns {Promise<User>}
237
238*/
239
240CodeGradX.Agent.prototype.processAuthentication = function () {
241 var agent = this;
242 function updateCredentials (user) {
243 agent.debug("Successful authentication of", user.email);
244 if ( agent.commands.options['update-credentials'] ) {
245 agent.debug("Updating credentials", agent.credentialsFile);
246 return CodeGradX.writeFileContent(
247 agent.credentialsFile,
248 JSON.stringify({
249 user: (agent.commands.options.user ||
250 agent.credentials.user),
251 password: (agent.commands.options.password ||
252 agent.credentials.password),
253 cookie: agent.state.currentCookie
254 }) ).then(function () {
255 return when(user);
256 }, function (reason) {
257 agent.debug("Could not write credentials");
258 return when.reject(reason);
259 });
260 } else {
261 return when(user);
262 }
263 }
264
265 function couldNotAuthenticate (reason) {
266 agent.debug("Failed authentication", reason);
267 return when.reject(reason);
268 }
269
270 // if a cookie is already known:
271 if ( agent.state.currentCookie ) {
272 agent.debug("Reusing already known cookie...");
273 return agent.state.sendAXServer('x', {
274 path: '/'
275 }).catch(function (reason) {
276 agent.debug("Obsolete or wrong cookie");
277 delete agent.state.currentCookie;
278 return agent.processAuthentication();
279 });
280 }
281
282 // --user= and --password= are present:
283 if ( agent.commands.options.user &&
284 agent.commands.options.password ) {
285 agent.debug("Authenticating with user and password...");
286 return agent.state.getAuthenticatedUser(
287 agent.commands.options.user,
288 agent.commands.options.password )
289 .then(updateCredentials)
290 .catch(couldNotAuthenticate);
291
292 // --credentials= designates a configuration file:
293 } else if ( agent.commands.options.credentials ) {
294 agent.debug("Reading credentials", agent.commands.options.credentials);
295 return CodeGradX.readFileContent(agent.commands.options.credentials)
296 .then(function (content) {
297 agent.debug("Read credentials" +
298 agent.commands.options.credentials);
299 agent.credentials = JSON.parse(content);
300 // A user and its password are present:
301 if ( agent.credentials.user &&
302 agent.credentials.password ) {
303 agent.debug("Authentication using user and password");
304 return agent.state.getAuthenticatedUser(
305 agent.credentials.user,
306 agent.credentials.password )
307 .then(updateCredentials)
308 .catch(couldNotAuthenticate);
309
310 // But a cookie may be present:
311 } else if ( agent.credentials.cookie ) {
312 agent.debug("Authentication using cookie");
313 agent.state.currentCookie = agent.credentials.cookie;
314 return agent.state.getAuthenticatedUser(
315 agent.credentials.user,
316 agent.credentials.password )
317 .then(updateCredentials)
318 .catch(couldNotAuthenticate);
319
320 } else {
321 var error1 = new Error("Could not authenticate");
322 return couldNotAuthenticate(error1);
323 }
324 }, function () {
325 var error2 = new Error("Could not read credentials");
326 return couldNotAuthenticate(error2);
327 });
328 }
329 return when.reject(new Error("No way to authenticate!"));
330};
331
332/** Parse command line arguments and enrich the agent with them.
333 The agent will get a `commands` property. This method is only
334 used by processAuthentication.
335
336 @param {Array<string>} strings - command line arguments
337 @return {Agent}
338
339*/
340
341CodeGradX.Agent.prototype.getOptions = function (strings) {
342 var agent = this;
343 var commands = {
344 options: {
345 "send": false
346 }
347 };
348 try {
349 if ( strings ) {
350 commands = this.parser.parse(strings);
351 } else {
352 commands = this.parser.parseSystem();
353 }
354 } catch (exc) {
355 agent.debug("getOptions failure", exc);
356 throw("getOptions failure: " + exc);
357 }
358 //console.log(commands);
359 this.commands = commands;
360 return commands;
361};
362
363/** Store a report.
364 XML or HTML reports are numbered consecutively.
365
366 @param {string|Buffer} content - content of the file to write
367 @param {string} label - type of document
368 @param {string} suffix - suffix of the file to write
369 @returns {Promise<>}
370
371*/
372
373CodeGradX.Agent.prototype.writeReport = function (content, label, suffix) {
374 var agent = this;
375 label = ('-' + label) || '';
376 var file = agent.xmldir + '/' +
377 (++agent.counter) + label + '.' + suffix;
378 return CodeGradX.writeFileContent(file, content)
379 .then(function () {
380 agent.debug("Now written", file);
381 return when(file);
382 });
383};
384
385/** Determine the type of interaction with the infrastructure.
386 `type` may be `job`, `exercise` or `batch`. If no type is
387 given, return a `Promise<User>`.
388
389 @returns {Promise<???>} yield a promise according to type
390
391*/
392
393CodeGradX.Agent.prototype.processType = function (user) {
394 var agent = this;
395 var type = agent.commands.options.type;
396 if ( type === 'job' ) {
397 return agent.processJob();
398 } else if ( type === 'exercise' ) {
399 return agent.processExercise();
400 } else if ( type === 'batch' ) {
401 return agent.processBatch();
402 } else if ( type === 'resume' ) {
403 return agent.processResume();
404 } else {
405 return when(agent.state.currentUser);
406 }
407};
408
409/** Determine an exercise. An exercise may be specified by a safecookie
410 (a long chain of characters starting with `U`) or by a kind of URI
411 telling where to find that safecookie. Syntaxes for that URI are:
412
413 file:exerciseAuthorReport.xml means the exercise mentioned.
414
415 campaign:free#N means the Nth exercise of campaign `free`
416
417 @param {string} string - a safecookie or a kind of URI
418 @returns {Promise<Exercise>}
419*/
420
421CodeGradX.Agent.prototype.guessExercise = function (string) {
422 var agent = this;
423 var exercise, index;
424
425 // file: should prefix an exerciseAuthorReport XML file:
426 var results = string.match(/^file:(.*)$/);
427 if ( results ) {
428 var file = results[1];
429 index = results[3] || 0;
430 var content = fs.readFileSync(file, 'utf8');
431 results = content.match(/<exerciseAuthorReport .* safecookie="([^"]+)"/);
432 if ( results ) {
433 exercise = new CodeGradX.Exercise({
434 safecookie: results[1]
435 });
436 return when(exercise);
437 } else {
438 return when.reject("Could not find safecookie within " + string);
439 }
440 }
441
442 // campaign: prefixes the name of a campaign:
443 results = string.match(/^campaign:(.+)#(\d+)$/);
444 if ( results ) {
445 var campaignName = results[1];
446 index = results[2] / 1;
447 var user = agent.state.currentUser;
448 return user.getCampaign(campaignName)
449 .then(function (campaign) {
450 return campaign.getExercise(index)
451 .then(function (exercise) {
452 if ( exercise ) {
453 return when(exercise);
454 } else {
455 var error = new Error("Cannot find exercise#" + index +
456 " of the " + campaign.name + " campaign");
457 return when.reject(error);
458 }
459 });
460 });
461 }
462 exercise = new CodeGradX.Exercise({
463 safecookie: string
464 });
465 return when(exercise);
466};
467
468/** Send a Job and wait for the marking report
469
470 @returns {Promise<Job>}
471
472*/
473
474CodeGradX.Agent.prototype.processJob = function () {
475 var agent = this;
476 return agent.guessExercise(agent.commands.options.exercise)
477 .then(function (exercise) {
478 //console.log(exercise);
479 var parameters = {
480 progress: function (parameters) {
481 agent.debug("Waiting", parameters.i, "...");
482 }
483 };
484 if ( agent.commands.options.timeout ) {
485 parameters.step = agent.commands.options.timeout;
486 }
487 if ( agent.commands.options.retry ) {
488 parameters.attempts = agent.commands.options.retry;
489 }
490 function cannotSendAnswer (reason) {
491 agent.state.log.debug("Could not send file").show();
492 throw reason;
493 }
494 function getJobReport (job) {
495 agent.debug("Job sent, known as", job.jobid);
496 return agent.writeReport(job.responseXML,
497 'jobSubmittedReport',
498 'xml')
499 .then(function () {
500 agent.debug("Waiting for marking report");
501 return job.getReport(parameters)
502 .catch(_.bind(agent.cannotGetReport, agent))
503 .then(_.bind(agent.storeJobReports, agent))
504 .catch(_.bind(agent.cannotStoreReport, agent));
505 }).catch(_.bind(agent.cannotStoreReport, agent));
506 }
507 agent.debug("Sending job...");
508 return exercise
509 .sendFileAnswer(agent.commands.options.stuff)
510 .catch(cannotSendAnswer)
511 .then(getJobReport);
512 });
513};
514
515/** storeJobReports store the XML and HTML content of a job in the
516 `xmldir` directory. When the file is written, control is passed
517 to the returned promise.
518
519 @param {Job} job
520 @returns {Promise<nothing>}
521
522 */
523
524CodeGradX.Agent.prototype.storeJobReports = function (job) {
525 var agent = this;
526 agent.debug("Got job marking report", job.jobid);
527 function storeXMLReport (job) {
528 agent.debug("writing XML report");
529 return agent.writeReport(job.XMLreport,
530 'jobStudentReport',
531 'xml');
532 }
533 function storeHTMLReport (job) {
534 agent.debug("writing HTML report");
535 return agent.writeReport(job.HTMLreport,
536 'jobStudentReport',
537 'html');
538 }
539 function returnJob () {
540 return when(job);
541 }
542 return storeXMLReport(job)
543 .then(returnJob)
544 .catch(_.bind(agent.cannotStoreReport, agent))
545 .then(storeHTMLReport)
546 .catch(_.bind(agent.cannotStoreReport, agent))
547 .then(returnJob);
548};
549
550CodeGradX.Agent.prototype.cannotStoreReport = function (reason) {
551 var agent = this;
552 agent.debug("Could not store report", reason);
553 throw reason;
554};
555
556CodeGradX.Agent.prototype.cannotGetReport = function (reason) {
557 var agent = this;
558 agent.debug("Could not get report", reason);
559 throw reason;
560};
561
562/** Store a Batch report (even not completed): a multiJobStudentReport
563 XML document.
564
565 @param {Batch} batch
566 @returns {Promise<Batch>}
567
568*/
569
570CodeGradX.Agent.prototype.storeBatchReport = function (batch) {
571 var agent = this;
572 agent.debug("Got batch final report", batch.batchid);
573 function returnBatch () {
574 return when(batch);
575 }
576 function fetchJobs () {
577 if ( agent.commands.options.follow ) {
578 agent.debug("Fetching individual jobs...");
579 var promise, promises = [];
580 _.forEach(batch.jobs, function (job) {
581 if ( ! job._stored ) {
582 promise = job.getReport()
583 .then(_.bind(agent.storeJobReports, agent));
584 promises.push(promise);
585 }
586 });
587 return when.all(promises);
588 } else {
589 return when(batch);
590 }
591 }
592 return agent.writeReport(batch.XMLreport,
593 'multiJobStudentReport',
594 'xml')
595 .then(fetchJobs)
596 .then(returnBatch);
597};
598
599CodeGradX.Agent.prototype.storeExerciseReport = function (exercise) {
600 var agent = this;
601 function returnExercise () {
602 return when(exercise);
603 }
604 function fetchPseudoJobs () {
605 if ( agent.commands.options.follow ) {
606 agent.debug("Fetching individual pseudo-jobs...");
607 var promise, promises = [];
608 _.forEach(exercise.pseudojobs, function (job) {
609 promise = job.getReport()
610 .then(_.bind(agent.storeJobReports, agent));
611 promises.push(promise);
612 });
613 return when.all(promises);
614 }
615 return when(exercise);
616 }
617 agent.debug("Got exercise autocheck report");
618 return agent.writeReport(exercise.XMLauthorReport,
619 'exerciseAuthorReport',
620 'xml')
621 .then(fetchPseudoJobs)
622 .then(returnExercise);
623};
624
625/** Examine a Batch (even not completed) and fetch the grading reports
626 of already graded jobs. Use the `_stored` tag to fetch only once
627 the report.
628
629 @param {Batch} batch
630 @returns {Promise<Batch>}
631
632*/
633
634CodeGradX.Agent.prototype.fetchJobs = function (batch) {
635 var agent = this;
636 function returnBatch () {
637 return when(batch);
638 }
639 if ( agent.commands.options.follow &&
640 batch.finishedjobs > 0 ) {
641 agent.debug("Fetching in advance individual jobs...");
642 var promise, promises = [];
643 _.forEach(batch.jobs, function (job) {
644 if ( ! job._stored ) {
645 promise = job.getReport()
646 .then(_.bind(agent.storeJobReports, agent))
647 .then(function (job) {
648 agent.debug("Stored report for job " + job.jobid);
649 job._stored = true;
650 return promise.done(job);
651 });
652 promises.push(promise);
653 }
654 });
655 when.all(promises).then(returnBatch).catch(ignore);
656 }
657};
658
659/** Send a Batch and wait for the associated marking reports
660
661 @returns {Promise<Batch>}
662
663*/
664
665CodeGradX.Agent.prototype.processBatch = function () {
666 var agent = this;
667 var exercise = new CodeGradX.Exercise({
668 safecookie: agent.commands.options.exercise
669 });
670 function showProgress (parameters) {
671 var batch = parameters.batch;
672 function ignore () { return; }
673 if ( batch ) {
674 agent.debug("Marked jobs:", batch.finishedjobs,
675 '/', (batch.totaljobs || '?'),
676 ' still waiting...');
677 //agent.state.log.show();
678 // Fetch job reports for already marked jobs:
679 agent.fetchJobs(batch);
680 } else {
681 agent.debug("Waiting...", parameters.i);
682 }
683 }
684 var parameters = {
685 progress: showProgress
686 };
687 if ( agent.commands.options.timeout ) {
688 parameters.step = agent.commands.options.timeout;
689 }
690 if ( agent.commands.options.retry ) {
691 parameters.attempts = agent.commands.options.retry;
692 }
693 function cannotSendBatch (reason) {
694 agent.state.log.debug("Could not send batch file");
695 throw reason;
696 }
697 function getBatchReport (batch) {
698 agent.debug("Batch sent, known as", batch.batchid);
699 return agent.writeReport(batch.responseXML,
700 'multiJobSubmittedReport',
701 'xml')
702 .then(function () {
703 agent.debug("Waiting for final batch completion report...");
704 return batch.getFinalReport(parameters)
705 .catch(_.bind(agent.cannotGetReport, agent))
706 .then(_.bind(agent.storeBatchReport, agent))
707 .catch(_.bind(agent.cannotStoreReport, agent));
708 }).catch(_.bind(agent.cannotStoreReport, agent));
709 }
710 agent.debug("Sending batch...");
711 return exercise
712 .sendBatch(agent.commands.options.stuff)
713 .catch(cannotSendBatch)
714 .then(getBatchReport);
715};
716
717/** Send an Exercise and wait for the autocheck report
718
719 @returns {Promise<Exercise>}
720
721*/
722
723CodeGradX.Agent.prototype.processExercise = function () {
724 var agent = this;
725 var parameters = {};
726 if ( agent.commands.options.timeout ) {
727 parameters.step = agent.commands.options.timeout;
728 }
729 if ( agent.commands.options.retry ) {
730 parameters.attempts = agent.commands.options.retry;
731 }
732 function cannotSendExercise (reason) {
733 agent.state.log.debug("Could not send exercise file");
734 throw reason;
735 }
736 function getExerciseReport (exercise) {
737 agent.debug("Exercise sent, known as", exercise.name);
738 return agent.writeReport(exercise.XMLsubmission,
739 "exerciseSubmittedReport",
740 "xml")
741 .then(function () {
742 agent.debug("Waiting for exercise autocheck report...");
743 return exercise.getExerciseReport(parameters)
744 .catch(_.bind(agent.cannotGetReport, agent))
745 .then(_.bind(agent.storeExerciseReport, agent))
746 .catch(_.bind(agent.cannotStoreReport, agent));
747 }).catch(_.bind(agent.cannotStoreReport, agent));
748 }
749 agent.debug("Sending exercise...");
750 return agent.state.currentUser
751 .submitNewExercise(agent.commands.options.stuff)
752 .catch(cannotSendExercise)
753 .then(getExerciseReport);
754};
755
756/** Resume a former process. A previous exercise or batch was not finished
757 so resume it.
758
759 @returns {Promise<???>}
760
761*/
762
763CodeGradX.Agent.prototype.processResume = function () {
764 var agent = this;
765 var file = agent.commands.options.resume;
766 var content = fs.readFileSync(file, 'utf8');
767 if ( content.match(/<multiJobSubmittedReport /) ) {
768 return agent.processResumeBatch(content);
769 } else if ( content.match(/<exerciseSubmittedReport/) ) {
770 return agent.processResumeExercise(content);
771 } else {
772 return when.reject(new Error("Cannot resume " + file));
773 }
774};
775
776/** Resume a former processBatch that is wait for the completion of the
777 Batch and fetch all the students' jobs.
778
779 @returns {Promise<Batch>}
780
781*/
782
783CodeGradX.Agent.prototype.processResumeBatch = function (content) {
784 var agent = this;
785 function showProgress (parameters) {
786 var batch = parameters.batch;
787 function ignore () { return; }
788 if ( batch ) {
789 agent.debug("Marked jobs:", (batch.finishedjobs || '?'),
790 '/', (batch.totaljobs || '?'),
791 ' still waiting...');
792 //agent.state.log.show();
793 // Fetch job reports for already marked jobs:
794 agent.fetchJobs(batch);
795 } else {
796 agent.debug("Waiting...", parameters.i);
797 }
798 }
799 var parameters = {
800 progress: showProgress
801 };
802 if ( agent.commands.options.timeout ) {
803 parameters.step = agent.commands.options.timeout;
804 }
805 if ( agent.commands.options.retry ) {
806 parameters.attempts = agent.commands.options.retry;
807 }
808 return CodeGradX.parsexml(content).then(function (js) {
809 js = js.fw4ex.multiJobSubmittedReport;
810 var batch = new CodeGradX.Batch({
811 responseXML: content,
812 response: js,
813 personid: CodeGradX._str2num(js.person.$.personid),
814 archived: CodeGradX._str2Date(js.batch.$.archived),
815 batchid: js.batch.$.batchid,
816 pathdir: js.$.location
817 });
818 return batch.getFinalReport(parameters)
819 .catch(_.bind(agent.cannotGetReport, agent))
820 .then(_.bind(agent.storeBatchReport, agent))
821 .catch(_.bind(agent.cannotStoreReport, agent));
822 });
823};
824
825CodeGradX.Agent.prototype.processResumeExercise = function (content) {
826 var agent = this;
827 var parameters = {};
828 if ( agent.commands.options.timeout ) {
829 parameters.step = agent.commands.options.timeout;
830 }
831 if ( agent.commands.options.retry ) {
832 parameters.attempts = agent.commands.options.retry;
833 }
834 return CodeGradX.parsexml(content).then(function (js) {
835 js = js.fw4ex.exerciseSubmittedReport;
836 var exercise = new CodeGradX.Exercise({
837 location: js.$.location,
838 personid: CodeGradX._str2num(js.person.$.personid),
839 exerciseid: js.exercise.$.exerciseid,
840 XMLsubmission: content
841 });
842 return exercise.getExerciseReport(parameters)
843 .catch(_.bind(agent.cannotGetReport, agent))
844 .then(_.bind(agent.storeExerciseReport, agent))
845 .catch(_.bind(agent.cannotStoreReport, agent));
846 });
847};
848
849/* *********************************************************************
850 Determine whether this module is used as a script or as a library.
851 If used as a script then process the arguments otherwise do nothing.
852*/
853
854if ( _.endsWith(process.argv[1], 'codegradxagent.js') ) {
855 // We are running that script:
856 var agent = new CodeGradX.Agent();
857 try {
858 return agent.process(process.argv.slice(2));
859 } catch (exc) {
860 console.log('Failure: ' + exc);
861 }
862}
863
864// end of codegradxagent.js
865