Mwalker has submitted this change and it was merged.

Change subject: Improve status reporting.
......................................................................


Improve status reporting.

Add more granular status reporting; refactor the 'status' module.

Change-Id: I83457a8b5424d3bc035ee4870047c2c7531ea7c7
---
M lib/db.js
M lib/index.js
M lib/status.js
3 files changed, 112 insertions(+), 42 deletions(-)

Approvals:
  Mwalker: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/lib/db.js b/lib/db.js
index d331321..3e0668d 100644
--- a/lib/db.js
+++ b/lib/db.js
@@ -28,6 +28,18 @@
        }).then(function() { return db; });
 };
 
+// Returns a promise for the number of keys (used to compute status 
percentages)
+Db.prototype.count = function() {
+       return this.db.then(function(db) {
+               return P.call(
+                       db.get, db,
+                       "SELECT count() AS count FROM kv_table;"
+               );
+       }).then(function(row) {
+               return row.count;
+       });
+};
+
 // Returns a promise for the value.
 Db.prototype.get = function(key, nojson) {
        return this.db.then(function(db) {
diff --git a/lib/index.js b/lib/index.js
index cb9cc45..c78c63e 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -8,7 +8,6 @@
 var gammalatex = require('gammalatex');
 var guard = require('when/guard');
 var path = require('path');
-var status = require('./status');
 var stream = require('stream');
 var tmp = require('tmp');
 var url = require('url');
@@ -23,6 +22,7 @@
 var Db = require('./db');
 var DomUtil = require('./domutil');
 var P = require('./p');
+var StatusReporter = require('./status');
 
 var STD_HEADER = [
        "%!TEX TS-program = xelatex",
@@ -532,17 +532,15 @@
 // return a promise for the builddir and control file contents
 // (after the bundle has been unpacked)
 var unpackBundle = function(options) {
-       var metabook, builddir;
+       var metabook, builddir, status = options.status;
 
-       status.createStage(0);
+       status.createStage(0, 'Unpacking content bundle');
 
        // first create a temporary directory
        return P.call(tmp.dir, tmp, {
                prefix: json.name,
                unsafeCleanup: !(options.debug || options.latex)
        }).then(function(_builddir) {
-               status.report('Reading data bundle for document construction');
-
                builddir = _builddir;
                // make bundle and latex subdirs
                return when.join(
@@ -552,7 +550,6 @@
        }).then(function() {
                // now unpack the zip archive
                var bundledir = path.join(builddir, 'bundle');
-               options.log('Unpacking bundle in', bundledir);
                return P.spawn('unzip', [ path.resolve( options.bundle ) ], {
                        cwd: bundledir
                });
@@ -643,23 +640,27 @@
 // return a promise for a map from file resource URLs to on-disk filenames
 // (after image processing / renaming has been done)
 var processImages = function(metabook, builddir, options) {
-       options.log('Processing images');
+       var status = options.status;
        var imagedir = path.join(builddir, 'bundle', 'images');
        var imagemap = new Map();
        var imagedb = new Db(
                path.join(builddir, 'bundle', 'imageinfo.db'), { readonly: true 
}
        );
-       var p = when.resolve();
-
-       status.createStage(imagedb.length);
+       var p = imagedb.count().then(function(n) {
+               status.createStage(n, 'Processing media files');
+       });
        return imagedb.forEach(function(key, val) {
-               status.report('Processing media files for inclusion', 
val.filename);
+               var filename = val.filename;
+               p = p.then(function() {
+                       // status reporting is serialized
+                       status.report(null, filename || '');
+               });
+               if (!filename) { return; }
                if (!/^https?:\/\//.test(key)) {
                        // compatibility with pediapress format
                        key = val.resource;
                }
-               var filename = val.filename;
-               if (!filename) { return; }
+               // conversion/rename happens in parallel (new promise 'pp')
                var pp = when.resolve({ imagedir: imagedir, filename: filename 
});
                // convert gifs to pngs
                if (val.mime === 'image/gif') {
@@ -687,8 +688,19 @@
        });
 };
 
+// count total # of items (used for status reporting)
+var countItems = function(item) {
+       return (item.items || []).reduce(function(sum, item) {
+               return sum + countItems(item);
+       }, 1);
+};
+
 // Return an empty promise after the output.tex file has been written.
 var generateLatex = function(metabook, builddir, imagemap, options) {
+       var status = options.status;
+       status.createStage(countItems(metabook), 'Processing collection');
+       status.report(null, metabook.title);
+
        var output = fs.createWriteStream(path.join(builddir, 'output.tex'), {
                encoding: 'utf8'
        });
@@ -747,7 +759,7 @@
        var write = {};
        write.article = function(item) {
                console.assert(item.type === 'article');
-               options.log('Writing article', item.title);
+               status.report(null, item.title);
                var revid = item.revision;
                var document, base = '';
                var key = (item.wiki ? (item.wiki+'|') : '') + revid;
@@ -779,6 +791,7 @@
        };
        write.chapter = function(item) {
                console.assert(item.type === 'chapter');
+               status.report(null, item.title);
                if ('columns' in item && columns !== item.columns) {
                        columns = item.columns;
                        output.write(columns === 1 ? '\\onecolumn\n' : 
'\\twocolumn\n');
@@ -787,8 +800,6 @@
                return P.forEachSeq(item.items, write.article);
        };
 
-       status.createStage(1 /* XXX This should be the total number of nodes 
we're going to visit */);
-       status.report('Traversing page DOM', metabook.title); /* XXX Call this 
for every node */
        return P.forEachSeq(metabook.items, function(item) {
                return write[item.type](item);
        }).then(function() {
@@ -799,9 +810,8 @@
 // Return an empty promise after the latex has been either written or
 // compiled to a PDF.
 var compileLatex = function(builddir, options) {
-       options.log('Compiling to PDF with xelatex');
-       status.createStage(0);
-       status.report('Compiling to PDF with xelatex');
+       var status = options.status;
+       status.createStage(0, 'Compiling PDF');
 
        gammalatex.setCompileCommand({
                command: "xelatex",
@@ -831,13 +841,11 @@
        writeStream.on('close', function() { deferred.resolve(); });
 
        if (options.latex) {
-               options.log('Writing LaTeX');
                writeStream.end(latexOutput, 'utf8');
        } else {
                P.call(gammalatex.parse, gammalatex, latexOutput).
                        then(function(args) {
                                var readStream = args[0];
-                               options.log('Saving PDF');
                                readStream.pipe(writeStream);
                        }).done();
        }
@@ -847,7 +855,12 @@
 // Return a promise for an exit status (0 for success) after the bundle
 // specified in the options has been converted.
 var convert = function(options) {
-       status.setNumStages(4); // update this number if we add more stages
+       var status = options.status = new StatusReporter(4, function(msg) {
+               if (options.log) {
+                       var file = msg.file ? (': ' + msg.file) : '';
+                       options.log('['+msg.percent.toFixed()+'%]', msg.status 
+ file);
+               }
+       });
        var metabook, builddir, imagemap;
        return when.resolve().then(function() {
                // unpack the bundle
@@ -867,13 +880,14 @@
                // compile it to PDF
                return compileLatex(builddir, options);
        }).then(function() {
-               options.log('Done.');
+               status.createStage(0, 'Done');
                return 0; // success!
        }, function(err) {
                // xxx clean up?
                if (options.debug) {
                        throw err;
                }
+               // xxx send this error to parent process?
                console.error('Error:', err);
                return 1;
        });
diff --git a/lib/status.js b/lib/status.js
index d5a4c04..c3b0a54 100644
--- a/lib/status.js
+++ b/lib/status.js
@@ -1,25 +1,69 @@
-var percentComplete = 0;
-var currentStage = 0.0;
-var stagesInv = 1.0;
-var stageLen = 0;
+/* Progress reporting using the node IPC mechanism. */
 
-module.exports.setNumStages = function(num) {
-       stagesInv = 1.0 / num;
-};
-
-module.exports.createStage = function(len) {
-       percentComplete = currentStage * stagesInv;
-       currentStage += 1;
-       if (len) {
-               stageLen = 1.0 / len;
-       } else {
-               stageLen = 0;
+var StatusReporter = module.exports = function(numStages, extraLog) {
+       this.extraLog = extraLog;
+       this.percentComplete = 0;
+       this.currentStage = 0.0;
+       this.stagesInv = 1.0;
+       this.stageLen = 0;
+       this.message = '';
+       this.file = undefined;
+       if (numStages) {
+               this._setNumStages(numStages);
        }
 };
 
-module.exports.report = function(message, file) {
-       percentComplete += 100.0 * (stagesInv * stageLen);
+/** Send one report with current status (internal function). */
+StatusReporter.prototype._send = function() {
+       var msg = {
+               status: this.message,
+               file: this.file,
+               percent: this.percentComplete
+       };
+       if (this.extraLog) {
+               this.extraLog(msg);
+       }
        if (process.send) {
-               process.send(JSON.stringify({status: message, file: file, 
percent: percentComplete}));
+               process.send(msg);
        }
-};
\ No newline at end of file
+};
+
+/** Update current status with message/file, and send it. */
+StatusReporter.prototype._report = function(message, file) {
+       if (message || message==='') {
+               this.message = message;
+               this.file = file;
+       } else if (file) {
+               // update only the current file
+               this.file = file;
+       }
+       this._send();
+};
+
+/** Initialize StatusReporter with given # of stages. */
+StatusReporter.prototype._setNumStages = function(num) {
+       this.stagesInv = 1.0 / num;
+};
+
+/** Start a new stage, which will be `opt_len` steps long. */
+StatusReporter.prototype.createStage = function(opt_len, opt_message, 
opt_file) {
+       this.percentComplete = 100.0 * this.currentStage * this.stagesInv;
+       this.currentStage += 1;
+       if (opt_len) {
+               this.stageLen = 1.0 / opt_len;
+       } else {
+               this.stageLen = 0;
+       }
+       this._report(opt_message || '', opt_file);
+};
+
+/** Advance the stage by N steps. */
+StatusReporter.prototype.reportN = function(n, opt_message, opt_file) {
+       this._report(opt_message, opt_file);
+       this.percentComplete += 100.0 * (this.stagesInv * this.stageLen * n);
+};
+
+/** Advance the stage by 1. */
+StatusReporter.prototype.report = function(opt_message, opt_file) {
+       this.reportN(1, opt_message, opt_file);
+};

-- 
To view, visit https://gerrit.wikimedia.org/r/97740
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I83457a8b5424d3bc035ee4870047c2c7531ea7c7
Gerrit-PatchSet: 1
Gerrit-Project: 
mediawiki/extensions/Collection/OfflineContentGenerator/latex_renderer
Gerrit-Branch: master
Gerrit-Owner: Cscott <canan...@wikimedia.org>
Gerrit-Reviewer: Mwalker <mwal...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to