From 44a0f87a2470af5c482878d368ea4876705f1f0a Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Tue, 28 Jul 2015 22:31:28 -0600 Subject: [PATCH] + 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 --- core/art.js | 2 +- core/menu_module.js | 55 ++++++++++++++++++++++++++++++++-- core/stats.js | 32 ++++++++++++++++++++ core/theme.js | 4 +-- mods/last_callers.js | 70 ++++++++++++++++++-------------------------- 5 files changed, 116 insertions(+), 47 deletions(-) create mode 100644 core/stats.js diff --git a/core/art.js b/core/art.js index 36479177..b69f45c3 100644 --- a/core/art.js +++ b/core/art.js @@ -244,7 +244,7 @@ function getArtFromPath(path, options, cb) { } return result; - } + } if(options.readSauce === true) { readSAUCE(data, function onSauce(err, sauce) { diff --git a/core/menu_module.js b/core/menu_module.js index 997f9b53..02e91dca 100644 --- a/core/menu_module.js +++ b/core/menu_module.js @@ -84,6 +84,18 @@ function MenuModule(options) { 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) { self.mciReady(mciData); 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); @@ -144,19 +181,29 @@ MenuModule.prototype.standardMCIReadyHandler = function(mciData) { // * Standard/prefdefined MCI entries must load both (e.g. %BN is expected to resolve) // var self = this; + var vcCount = 0; + var vcReady = 0; _.forEach(mciData, function entry(mciMap, name) { assert('menu' === name || 'prompt' === name); + ++vcCount; self.addViewController(name, new ViewController( { client : self.client } )); }); - + var viewsReady = function(err) { // :TODO: what should really happen here? if(err) { self.client.log.warn(err); } + + ++vcReady; + if(vcReady === vcCount) { + self.allViewsReady(); + } + }; + if(self.viewControllers.menu) { var menuLoadOpts = { mciMap : mciData.menu, @@ -188,6 +235,7 @@ MenuModule.prototype.finishedLoading = function() { self.client.gotoMenuModule( { name : self.menuConfig.next } ); }, this.menuConfig.options.nextTimeout); } else { + /* var nextAction = function() { if(!_.isObject(self.menuConfig.form) && !_.isString(self.menuConfig.prompt) && _.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. theme.displayThemedPause( { client : self.client }, function keyPressed() { nextAction(); @@ -204,5 +254,6 @@ MenuModule.prototype.finishedLoading = function() { } else { nextAction(); } + */ } }; \ No newline at end of file diff --git a/core/stats.js b/core/stats.js new file mode 100644 index 00000000..b6e82592 --- /dev/null +++ b/core/stats.js @@ -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); + } + ); +} \ No newline at end of file diff --git a/core/theme.js b/core/theme.js index 91b0fdba..ebc143a5 100644 --- a/core/theme.js +++ b/core/theme.js @@ -152,7 +152,7 @@ function getThemeArt(name, themeID, options, cb) { options.random = miscUtil.valueWithDefault(options.random, true); 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) { // try fallback of art directory options.basePath = Config.paths.art; @@ -227,7 +227,7 @@ function displayThemedPause(options, cb) { callback(new Error('Missing standard \'pause\' prompt')) } } - }); + }); }, function displayPausePrompt(callback) { displayThemedAsset( diff --git a/mods/last_callers.js b/mods/last_callers.js index e64dcf0c..8587e6a9 100644 --- a/mods/last_callers.js +++ b/mods/last_callers.js @@ -1,22 +1,23 @@ /* jslint node: true */ 'use strict'; -var MenuModule = require('../core/menu_module.js').MenuModule; -var userDb = require('../core/database.js').dbs.user; -var ViewController = require('../core/view_controller.js').ViewController; -var TextView = require('../core/text_view.js').TextView; +var MenuModule = require('../core/menu_module.js').MenuModule; +var userDb = require('../core/database.js').dbs.user; +var ViewController = require('../core/view_controller.js').ViewController; +var TextView = require('../core/text_view.js').TextView; +var getUserLoginHistory = require('../core/stats.js').getUserLoginHistory; -var util = require('util'); -var moment = require('moment'); -var async = require('async'); -var assert = require('assert'); -var _ = require('lodash'); +var util = require('util'); +var moment = require('moment'); +var async = require('async'); +var assert = require('assert'); +var _ = require('lodash'); exports.moduleInfo = { name : 'Last Callers', desc : 'Last callers to the system', author : 'NuSkooler', - packageName : 'codes.l33t.enigma.lastcallers' + packageName : 'codes.l33t.enigma.lastcallers' // :TODO: concept idea for mods }; exports.getModule = LastCallersModule; @@ -59,8 +60,7 @@ LastCallersModule.prototype.mciReady = function(mciData) { var self = this; var vc = self.viewControllers.lastCallers = new ViewController( { client : self.client } ); - var lc = []; - var rows = self.rows; + var loginHistory; async.series( [ @@ -75,35 +75,21 @@ LastCallersModule.prototype.mciReady = function(mciData) { callback(err); }); }, - // :TODO: a public method of getLastCallers(count) would be better function fetchHistory(callback) { - userDb.each( - 'SELECT user_id, user_name, timestamp ' + - '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); - } - ); + getUserLoginHistory(self.rows, function historyRetrieved(err, lh) { + loginHistory = lh; + callback(err); + }); }, function fetchUserProperties(callback) { - async.each(lc, function callEntry(c, next) { + async.each(loginHistory, function entry(histEntry, next) { userDb.each( 'SELECT prop_name, prop_value ' + 'FROM user_property ' + 'WHERE user_id=? AND (prop_name="location" OR prop_name="affiliation");', - [ c.userId ], + [ histEntry.userId ], function propRow(err, propEntry) { - c[propEntry.prop_name] = propEntry.prop_value; + histEntry[propEntry.prop_name] = propEntry.prop_value; }, function complete(err) { next(); @@ -154,17 +140,17 @@ LastCallersModule.prototype.mciReady = function(mciData) { vc.addView(v); }; - lc.forEach(function lastCaller(c) { + loginHistory.forEach(function entry(histEntry) { if(row === views.who.position.row) { - views.who.setText(c.who); - views.location.setText(c.location); - views.affils.setText(c.affiliation); - views.when.setText(moment(c.when).format(self.dateTimeFormat)); + views.who.setText(histEntry.userName); + views.location.setText(histEntry.location); + views.affils.setText(histEntry.affiliation); + views.when.setText(moment(histEntry.timestamp).format(self.dateTimeFormat)); } else { - addView(views.who, c.who); - addView(views.location, c.location); - addView(views.affils, c.affiliation); - addView(views.when, moment(c.when).format(self.dateTimeFormat)); + addView(views.who, histEntry.userName); + addView(views.location, histEntry.location); + addView(views.affils, histEntry.affiliation); + addView(views.when, moment(histEntry.timestamp).format(self.dateTimeFormat)); } row++;