+ stats.js: public APIs for accessing various system stats. Probably needs a better name
* Fix pause placement. Wait for all views ready before placing cursor such that the prompt will display in the right spot
This commit is contained in:
parent
4a342ba2fa
commit
44a0f87a24
|
@ -244,7 +244,7 @@ function getArtFromPath(path, options, cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(options.readSauce === true) {
|
if(options.readSauce === true) {
|
||||||
readSAUCE(data, function onSauce(err, sauce) {
|
readSAUCE(data, function onSauce(err, sauce) {
|
||||||
|
|
|
@ -84,6 +84,18 @@ function MenuModule(options) {
|
||||||
callback(null);
|
callback(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
function recordCursorPosition(callback) {
|
||||||
|
if(self.shouldPause()) {
|
||||||
|
self.client.once('cursor position report', function cpr(pos) {
|
||||||
|
self.afterArtPos = pos;
|
||||||
|
self.client.log.trace( { position : pos }, 'After art position recorded');
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
self.client.term.write(ansi.queryPos());
|
||||||
|
} else {
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
function afterArtDisplayed(callback) {
|
function afterArtDisplayed(callback) {
|
||||||
self.mciReady(mciData);
|
self.mciReady(mciData);
|
||||||
callback(null);
|
callback(null);
|
||||||
|
@ -99,6 +111,31 @@ function MenuModule(options) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.shouldPause = function() {
|
||||||
|
return 'end' === self.menuConfig.pause || true === self.menuConfig.pause;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.allViewsReady = function() {
|
||||||
|
if(self.shouldPause()) {
|
||||||
|
self.client.term.write(ansi.goto(self.afterArtPos[0], 1));
|
||||||
|
|
||||||
|
// :TODO: really need a client.term.pause() that uses the correct art/etc.
|
||||||
|
theme.displayThemedPause( { client : self.client }, function keyPressed() {
|
||||||
|
self.nextAction();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.nextAction();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.nextAction = function() {
|
||||||
|
if(!_.isObject(self.menuConfig.form) && !_.isString(self.menuConfig.prompt) &&
|
||||||
|
_.isString(self.menuConfig.action))
|
||||||
|
{
|
||||||
|
menuUtil.handleAction(self.client, null, self.menuConfig);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
require('util').inherits(MenuModule, PluginModule);
|
require('util').inherits(MenuModule, PluginModule);
|
||||||
|
@ -144,19 +181,29 @@ MenuModule.prototype.standardMCIReadyHandler = function(mciData) {
|
||||||
// * Standard/prefdefined MCI entries must load both (e.g. %BN is expected to resolve)
|
// * Standard/prefdefined MCI entries must load both (e.g. %BN is expected to resolve)
|
||||||
//
|
//
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var vcCount = 0;
|
||||||
|
var vcReady = 0;
|
||||||
|
|
||||||
_.forEach(mciData, function entry(mciMap, name) {
|
_.forEach(mciData, function entry(mciMap, name) {
|
||||||
assert('menu' === name || 'prompt' === name);
|
assert('menu' === name || 'prompt' === name);
|
||||||
|
++vcCount;
|
||||||
self.addViewController(name, new ViewController( { client : self.client } ));
|
self.addViewController(name, new ViewController( { client : self.client } ));
|
||||||
});
|
});
|
||||||
|
|
||||||
var viewsReady = function(err) {
|
var viewsReady = function(err) {
|
||||||
// :TODO: what should really happen here?
|
// :TODO: what should really happen here?
|
||||||
if(err) {
|
if(err) {
|
||||||
self.client.log.warn(err);
|
self.client.log.warn(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++vcReady;
|
||||||
|
if(vcReady === vcCount) {
|
||||||
|
self.allViewsReady();
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if(self.viewControllers.menu) {
|
if(self.viewControllers.menu) {
|
||||||
var menuLoadOpts = {
|
var menuLoadOpts = {
|
||||||
mciMap : mciData.menu,
|
mciMap : mciData.menu,
|
||||||
|
@ -188,6 +235,7 @@ MenuModule.prototype.finishedLoading = function() {
|
||||||
self.client.gotoMenuModule( { name : self.menuConfig.next } );
|
self.client.gotoMenuModule( { name : self.menuConfig.next } );
|
||||||
}, this.menuConfig.options.nextTimeout);
|
}, this.menuConfig.options.nextTimeout);
|
||||||
} else {
|
} else {
|
||||||
|
/*
|
||||||
var nextAction = function() {
|
var nextAction = function() {
|
||||||
if(!_.isObject(self.menuConfig.form) && !_.isString(self.menuConfig.prompt) &&
|
if(!_.isObject(self.menuConfig.form) && !_.isString(self.menuConfig.prompt) &&
|
||||||
_.isString(self.menuConfig.action))
|
_.isString(self.menuConfig.action))
|
||||||
|
@ -196,7 +244,9 @@ MenuModule.prototype.finishedLoading = function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if('end' === self.menuConfig.pause || true === self.menuConfig.pause) {
|
if(self.shouldPause()) {
|
||||||
|
self.client.term.write(ansi.goto(self.afterArtPos[0], 1));
|
||||||
|
|
||||||
// :TODO: really need a client.term.pause() that uses the correct art/etc.
|
// :TODO: really need a client.term.pause() that uses the correct art/etc.
|
||||||
theme.displayThemedPause( { client : self.client }, function keyPressed() {
|
theme.displayThemedPause( { client : self.client }, function keyPressed() {
|
||||||
nextAction();
|
nextAction();
|
||||||
|
@ -204,5 +254,6 @@ MenuModule.prototype.finishedLoading = function() {
|
||||||
} else {
|
} else {
|
||||||
nextAction();
|
nextAction();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var userDb = require('./database.js').dbs.user;
|
||||||
|
|
||||||
|
var async = require('async');
|
||||||
|
|
||||||
|
exports.getUserLoginHistory = getUserLoginHistory;
|
||||||
|
|
||||||
|
function getUserLoginHistory(numRequested, cb) {
|
||||||
|
|
||||||
|
numRequested = Math.max(1, numRequested);
|
||||||
|
|
||||||
|
var loginHistory = [];
|
||||||
|
|
||||||
|
userDb.each(
|
||||||
|
'SELECT user_id, user_name, timestamp ' +
|
||||||
|
'FROM user_login_history ' +
|
||||||
|
'ORDER BY timestamp DESC ' +
|
||||||
|
'LIMIT ' + numRequested + ';',
|
||||||
|
function historyRow(err, histEntry) {
|
||||||
|
loginHistory.push( {
|
||||||
|
userId : histEntry.user_id,
|
||||||
|
userName : histEntry.user_name,
|
||||||
|
timestamp : histEntry.timestamp,
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
function complete(err, recCount) {
|
||||||
|
cb(err, loginHistory);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
|
@ -152,7 +152,7 @@ function getThemeArt(name, themeID, options, cb) {
|
||||||
options.random = miscUtil.valueWithDefault(options.random, true);
|
options.random = miscUtil.valueWithDefault(options.random, true);
|
||||||
options.basePath = paths.join(Config.paths.themes, themeID);
|
options.basePath = paths.join(Config.paths.themes, themeID);
|
||||||
|
|
||||||
art.getArt(name, options, function onThemeArt(err, artInfo) {
|
art.getArt(name, options, function onThemeArt(err, artInfo) {
|
||||||
if(err) {
|
if(err) {
|
||||||
// try fallback of art directory
|
// try fallback of art directory
|
||||||
options.basePath = Config.paths.art;
|
options.basePath = Config.paths.art;
|
||||||
|
@ -227,7 +227,7 @@ function displayThemedPause(options, cb) {
|
||||||
callback(new Error('Missing standard \'pause\' prompt'))
|
callback(new Error('Missing standard \'pause\' prompt'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function displayPausePrompt(callback) {
|
function displayPausePrompt(callback) {
|
||||||
displayThemedAsset(
|
displayThemedAsset(
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var MenuModule = require('../core/menu_module.js').MenuModule;
|
var MenuModule = require('../core/menu_module.js').MenuModule;
|
||||||
var userDb = require('../core/database.js').dbs.user;
|
var userDb = require('../core/database.js').dbs.user;
|
||||||
var ViewController = require('../core/view_controller.js').ViewController;
|
var ViewController = require('../core/view_controller.js').ViewController;
|
||||||
var TextView = require('../core/text_view.js').TextView;
|
var TextView = require('../core/text_view.js').TextView;
|
||||||
|
var getUserLoginHistory = require('../core/stats.js').getUserLoginHistory;
|
||||||
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
|
||||||
exports.moduleInfo = {
|
exports.moduleInfo = {
|
||||||
name : 'Last Callers',
|
name : 'Last Callers',
|
||||||
desc : 'Last callers to the system',
|
desc : 'Last callers to the system',
|
||||||
author : 'NuSkooler',
|
author : 'NuSkooler',
|
||||||
packageName : 'codes.l33t.enigma.lastcallers'
|
packageName : 'codes.l33t.enigma.lastcallers' // :TODO: concept idea for mods
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.getModule = LastCallersModule;
|
exports.getModule = LastCallersModule;
|
||||||
|
@ -59,8 +60,7 @@ LastCallersModule.prototype.mciReady = function(mciData) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var vc = self.viewControllers.lastCallers = new ViewController( { client : self.client } );
|
var vc = self.viewControllers.lastCallers = new ViewController( { client : self.client } );
|
||||||
var lc = [];
|
var loginHistory;
|
||||||
var rows = self.rows;
|
|
||||||
|
|
||||||
async.series(
|
async.series(
|
||||||
[
|
[
|
||||||
|
@ -75,35 +75,21 @@ LastCallersModule.prototype.mciReady = function(mciData) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// :TODO: a public method of getLastCallers(count) would be better
|
|
||||||
function fetchHistory(callback) {
|
function fetchHistory(callback) {
|
||||||
userDb.each(
|
getUserLoginHistory(self.rows, function historyRetrieved(err, lh) {
|
||||||
'SELECT user_id, user_name, timestamp ' +
|
loginHistory = lh;
|
||||||
'FROM user_login_history ' +
|
callback(err);
|
||||||
'ORDER BY timestamp DESC ' +
|
});
|
||||||
'LIMIT ' + rows + ';',
|
|
||||||
function historyRow(err, histEntry) {
|
|
||||||
lc.push( {
|
|
||||||
userId : histEntry.user_id,
|
|
||||||
who : histEntry.user_name,
|
|
||||||
when : histEntry.timestamp,
|
|
||||||
} );
|
|
||||||
},
|
|
||||||
function complete(err, recCount) {
|
|
||||||
rows = recCount; // adjust to retrieved
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
function fetchUserProperties(callback) {
|
function fetchUserProperties(callback) {
|
||||||
async.each(lc, function callEntry(c, next) {
|
async.each(loginHistory, function entry(histEntry, next) {
|
||||||
userDb.each(
|
userDb.each(
|
||||||
'SELECT prop_name, prop_value ' +
|
'SELECT prop_name, prop_value ' +
|
||||||
'FROM user_property ' +
|
'FROM user_property ' +
|
||||||
'WHERE user_id=? AND (prop_name="location" OR prop_name="affiliation");',
|
'WHERE user_id=? AND (prop_name="location" OR prop_name="affiliation");',
|
||||||
[ c.userId ],
|
[ histEntry.userId ],
|
||||||
function propRow(err, propEntry) {
|
function propRow(err, propEntry) {
|
||||||
c[propEntry.prop_name] = propEntry.prop_value;
|
histEntry[propEntry.prop_name] = propEntry.prop_value;
|
||||||
},
|
},
|
||||||
function complete(err) {
|
function complete(err) {
|
||||||
next();
|
next();
|
||||||
|
@ -154,17 +140,17 @@ LastCallersModule.prototype.mciReady = function(mciData) {
|
||||||
vc.addView(v);
|
vc.addView(v);
|
||||||
};
|
};
|
||||||
|
|
||||||
lc.forEach(function lastCaller(c) {
|
loginHistory.forEach(function entry(histEntry) {
|
||||||
if(row === views.who.position.row) {
|
if(row === views.who.position.row) {
|
||||||
views.who.setText(c.who);
|
views.who.setText(histEntry.userName);
|
||||||
views.location.setText(c.location);
|
views.location.setText(histEntry.location);
|
||||||
views.affils.setText(c.affiliation);
|
views.affils.setText(histEntry.affiliation);
|
||||||
views.when.setText(moment(c.when).format(self.dateTimeFormat));
|
views.when.setText(moment(histEntry.timestamp).format(self.dateTimeFormat));
|
||||||
} else {
|
} else {
|
||||||
addView(views.who, c.who);
|
addView(views.who, histEntry.userName);
|
||||||
addView(views.location, c.location);
|
addView(views.location, histEntry.location);
|
||||||
addView(views.affils, c.affiliation);
|
addView(views.affils, histEntry.affiliation);
|
||||||
addView(views.when, moment(c.when).format(self.dateTimeFormat));
|
addView(views.when, moment(histEntry.timestamp).format(self.dateTimeFormat));
|
||||||
}
|
}
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
|
|
Loading…
Reference in New Issue