+ 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:
Bryan Ashby 2015-07-28 22:31:28 -06:00
parent 4a342ba2fa
commit 44a0f87a24
5 changed files with 116 additions and 47 deletions

View File

@ -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,9 +181,12 @@ 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 } ));
}); });
@ -155,8 +195,15 @@ MenuModule.prototype.standardMCIReadyHandler = function(mciData) {
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();
} }
*/
} }
}; };

32
core/stats.js Normal file
View File

@ -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);
}
);
}

View File

@ -5,6 +5,7 @@ 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');
@ -16,7 +17,7 @@ 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 ' +
'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); 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++;