* Functional last callers mod

* Notes
* Some cleanup
This commit is contained in:
Bryan Ashby 2015-07-27 22:10:20 -06:00
parent 99ea870ebc
commit 4a342ba2fa
8 changed files with 126 additions and 222 deletions

View File

@ -91,6 +91,7 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
position : { row : mci.position[0], col : mci.position[1] }, position : { row : mci.position[0], col : mci.position[1] },
}; };
// :TODO: These should use setPropertyValue()!
function setOption(pos, name) { function setOption(pos, name) {
if(mci.args.length > pos && mci.args[pos].length > 0) { if(mci.args.length > pos && mci.args[pos].length > 0) {
options[name] = mci.args[pos]; options[name] = mci.args[pos];

View File

@ -40,29 +40,21 @@ function login(callingMenu, formData, extraArgs) {
}); });
}, },
function recordLoginHistory(callback) { function recordLoginHistory(callback) {
userDb.run(
'INSERT INTO user_login_history (user_id, user_name, timestamp) ' +
'VALUES(?, ?, ?);', [ user.userId, user.username, now.toISOString() ], function inserted(err) {
callback(err);
});
userDb.serialize(function serialized() {
userDb.run(
'INSERT INTO user_login_history (user_id, user_name, timestamp) ' +
'VALUES(?, ?, ?);', [ user.userId, user.username, now.toISOString() ]
);
/* // keep 30 days of records
userDb.run( userDb.run(
'DELETE FROM last_caller ' + 'DELETE FROM user_login_history ' +
'WHERE id NOT IN (' + 'WHERE timestamp <= DATETIME("now", "-30 day");'
' SELECT id ' + );
' FROM last_caller ' + });
' ORDER BY timestamp DESC ' +
' LIMIT 100);');
userDb.run( callback(null);
'DELETE FROM last_caller ' +
'WHERE user_id IN (' +
' SELECT user_id ' +
' ORDER BY timestamp DESC ' +
'LIMIT 1;')
*/
} }
], ],
function complete(err, results) { function complete(err, results) {

View File

@ -114,7 +114,6 @@ TextView.prototype.getData = function() {
}; };
TextView.prototype.setText = function(text) { TextView.prototype.setText = function(text) {
var widthDelta = 0; var widthDelta = 0;
if(this.text && this.text !== text) { if(this.text && this.text !== text) {
widthDelta = Math.abs(this.text.length - text.length); widthDelta = Math.abs(this.text.length - text.length);

View File

@ -67,7 +67,10 @@ function View(options) {
this.setDimension(options.dimens); this.setDimension(options.dimens);
this.autoScale = { height : false, width : false }; this.autoScale = { height : false, width : false };
} else { } else {
this.dimens = { width : 0, height : 0 }; this.dimens = {
width : options.width || 0,
height : 0
};
} }
// :TODO: Just use styleSGRx for these, e.g. styleSGR0, styleSGR1 = norm/focus // :TODO: Just use styleSGRx for these, e.g. styleSGR0, styleSGR1 = norm/focus
@ -214,6 +217,12 @@ View.prototype.setPropertyValue = function(propName, value) {
*/ */
break; break;
case 'resizable' :
if(_.isBoolean(value)) {
this.resizable = value;
}
break;
case 'argName' : this.submitArgName = value; break; case 'argName' : this.submitArgName = value; break;
} }

View File

@ -4,6 +4,7 @@
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 util = require('util'); var util = require('util');
var moment = require('moment'); var moment = require('moment');
@ -12,18 +13,16 @@ var assert = require('assert');
var _ = require('lodash'); var _ = require('lodash');
exports.moduleInfo = { exports.moduleInfo = {
name : 'Last Callers', name : 'Last Callers',
desc : 'Last 10 callers to the system', desc : 'Last callers to the system',
author : 'NuSkooler', author : 'NuSkooler',
packageName : 'codes.l33t.enigma.lastcallers'
}; };
exports.getModule = LastCallersModule; exports.getModule = LastCallersModule;
// :TODO: // :TODO:
// * Order should be menu/theme defined // * config.evenRowSGR (optional)
// * Text needs overflow defined (optional), e.g. "..."
// * Date/time format should default to theme short date + short time
// *
function LastCallersModule(options) { function LastCallersModule(options) {
MenuModule.call(this, options); MenuModule.call(this, options);
@ -31,66 +30,29 @@ function LastCallersModule(options) {
var self = this; var self = this;
this.menuConfig = options.menuConfig; this.menuConfig = options.menuConfig;
this.menuMethods = { this.rows = 10;
getLastCaller : function(formData, extraArgs) {
//console.log(self.lastCallers[self.lastCallerIndex]) if(this.menuConfig.config) {
var lc = self.lastCallers[self.lastCallerIndex++]; if(_.isNumber(this.menuConfig.config.rows)) {
var when = moment(lc.timestamp).format(self.menuConfig.config.dateTimeFormat); this.rows = Math.max(1, this.menuConfig.config.rows);
return util.format('%s %s %s %s', lc.name, lc.location, lc.affiliation, when);
} }
}; if(_.isString(this.menuConfig.config.dateTimeFormat)) {
this.dateTimeFormat = this.menuConfig.config.dateTimeFormat;
}
}
} }
util.inherits(LastCallersModule, MenuModule); util.inherits(LastCallersModule, MenuModule);
/*
LastCallersModule.prototype.enter = function(client) { LastCallersModule.prototype.enter = function(client) {
LastCallersModule.super_.prototype.enter.call(this, client); LastCallersModule.super_.prototype.enter.call(this, client);
var self = this; // we need the client to init this for theming
self.lastCallers = []; if(!_.isString(this.dateTimeFormat)) {
self.lastCallerIndex = 0; this.dateTimeFormat = this.client.currentTheme.helpers.getDateFormat('short') +
this.client.currentTheme.helpers.getTimeFormat('short');
var userInfoStmt = userDb.prepare( }
'SELECT prop_name, prop_value ' +
'FROM user_property ' +
'WHERE user_id=? AND (prop_name=? OR prop_name=?);');
var caller;
userDb.each(
'SELECT user_id, user_name, timestamp ' +
'FROM user_login_history ' +
'ORDER BY timestamp DESC ' +
'LIMIT 10;',
function userRows(err, userEntry) {
caller = {
who : userEntry.user_name,
when : userEntry.timestamp,
};
userInfoStmt.each( [ userEntry.user_id, 'location', 'affiliation' ], function propRow(err, propEntry) {
if(!err) {
caller[propEntry.prop_name] = propEntry.prop_value;
}
}, function complete(err) {
if(!err) {
self.lastCallers.push(caller);
}
});
}
);
}; };
*/
/*
LastCallersModule.prototype.mciReady = function(mciData) {
LastCallersModule.super_.prototype.mciReady.call(this, mciData);
// we do this so other modules can be both customized and still perform standard tasks
LastCallersModule.super_.prototype.standardMCIReadyHandler.call(this, mciData);
};
*/
LastCallersModule.prototype.mciReady = function(mciData) { LastCallersModule.prototype.mciReady = function(mciData) {
LastCallersModule.super_.prototype.mciReady.call(this, mciData); LastCallersModule.super_.prototype.mciReady.call(this, mciData);
@ -98,12 +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 lc = [];
var count = _.size(mciData.menu) / 4; var rows = self.rows;
if(count < 1) {
// :TODO: Log me!
return;
}
async.series( async.series(
[ [
@ -118,12 +75,13 @@ 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( userDb.each(
'SELECT user_id, user_name, timestamp ' + 'SELECT user_id, user_name, timestamp ' +
'FROM user_login_history ' + 'FROM user_login_history ' +
'ORDER BY timestamp DESC ' + 'ORDER BY timestamp DESC ' +
'LIMIT ' + count + ';', 'LIMIT ' + rows + ';',
function historyRow(err, histEntry) { function historyRow(err, histEntry) {
lc.push( { lc.push( {
userId : histEntry.user_id, userId : histEntry.user_id,
@ -132,7 +90,7 @@ LastCallersModule.prototype.mciReady = function(mciData) {
} ); } );
}, },
function complete(err, recCount) { function complete(err, recCount) {
count = recCount; // adjust to retrieved rows = recCount; // adjust to retrieved
callback(err); callback(err);
} }
); );
@ -156,124 +114,65 @@ LastCallersModule.prototype.mciReady = function(mciData) {
}); });
}, },
function createAndPopulateViews(callback) { function createAndPopulateViews(callback) {
assert(lc.length === count);
var rowsPerColumn = count / 4;
// //
// TL1...count = who // TL1 = who
// TL<count>...<count*2> = location // TL2 = location
// TL3 = affiliation
// TL4 = when
// //
var i; // These form the order/layout for a row. Additional rows
var v; // will use them as a template.
for(i = 0; i < rowsPerColumn; ++i) {
v = vc.getView(i + 1);
v.setText(lc[i].who);
}
for( ; i < rowsPerColumn * 2; ++i) {
v = vc.getView(i + 1);
v.setText(lc[i].location);
}
// //
var views = {
// 1..count/4 = who who : vc.getView(1),
// count/10 location : vc.getView(2),
affils : vc.getView(3),
/* when : vc.getView(4),
var viewOpts = {
client : self.client,
}; };
var rowViewId = 1; var row = views.who.position.row;
var v;
lc.forEach(function lcEntry(caller) {
v = vc.getView(rowViewId++);
self.menuConfig.config.fields.forEach(function field(f) { var nextId = 5;
switch(f.name) {
case 'who' :
} function addView(templateView, text) {
}); // :TODO: Is there a better way to clone this when dealing with instances?
var v = new TextView( {
client : self.client,
id : nextId++,
position : { row : row, col : templateView.position.col },
ansiSGR : templateView.ansiSGR,
textStyle : templateView.textStyle,
textOverflow : templateView.textOverflow,
dimens : templateView.dimens,
resizable : templateView.resizable,
} );
v.setText(caller.who) v.id = nextId++;
v.position.row = row;
v.setPropertyValue('text', text);
vc.addView(v);
};
lc.forEach(function lastCaller(c) {
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));
} 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));
}
row++;
}); });
*/
} }
], ],
function complete(err) { function complete(err) {
console.log(lc) self.client.log.error(err);
} }
); );
}; };
/*
LastCallersModule.prototype.mciReady = function(mciData) {
LastCallersModule.super_.prototype.mciReady.call(this, mciData);
var lastCallers = [];
var self = this;
// :TODO: durp... need a table just for this so dupes are possible
var userInfoStmt = userDb.prepare(
'SELECT prop_name, prop_value ' +
'FROM user_property ' +
'WHERE user_id=? AND (prop_name=? OR prop_name=?);');
var caller;
userDb.each(
'SELECT id, user_name, timestamp ' +
'FROM user_last_login ' +
'ORDER BY timestamp DESC ' +
'LIMIT 10;',
function userRows(err, userEntry) {
caller = { name : userEntry.user_name };
userInfoStmt.each(userEntry.id, 'location', 'affiliation', function propRow(err, propEntry) {
console.log(propEntry)
if(!err) {
caller[propEntry.prop_name] = propEntry.prop_value;
}
}, function complete(err) {
lastCallers.push(caller);
});
},
function complete(err) {
//
// TL1=name, TL2=location, TL3=affils
// TL4=name, TL5=location, ...
// ...
// TL28=name, TL29=location, TL30=affils
//
var lc = self.viewControllers.lastCallers = new ViewController( { client : self.client });
var loadOpts = {
callingMenu : self,
mciMap : mciData.menu,
noInput : true,
};
self.viewControllers.lastCallers.loadFromMenuConfig(loadOpts, function viewsReady(err) {
console.log(lastCallers);
var callerIndex = 0;
for(var i = 1; i < 30; i += 3) {
if(lastCallers.length > callerIndex) {
lc.getView(i).setText(lastCallers[callerIndex].name);
lc.getView(i + 1).setText(lastCallers[callerIndex].location);
lc.getView(i + 2).setText(lastCallers[callerIndex].affiliation);
++callerIndex;
} else {
}
}
});
}
);
};
*/

View File

@ -123,12 +123,10 @@
}, },
"ME3" : { "ME3" : {
"argName" : "birthdate", "argName" : "birthdate",
//"width" : 8,
"maskPattern" : "####/##/##" "maskPattern" : "####/##/##"
}, },
"ET4" : { "ET4" : {
"argName" : "sex", "argName" : "sex",
//"width" : 1,
"maxLength" : 1 "maxLength" : 1
}, },
"ET5" : { "ET5" : {
@ -212,37 +210,26 @@
"art" : "LASTCALL.ANS", "art" : "LASTCALL.ANS",
"options" : { "cls" : true }, "options" : { "cls" : true },
"config" : { "config" : {
"dateTimeFormat" : "ddd MMM Do H:mm a", "dateTimeFormat" : "ddd MMM Do H:mm a"
"fields" : [
{
"name" : "who",
"width" : 17
},
{
"name" : "location",
"width" : 20
},
{
"name" : "affiliation",
"width" : 20
},
{
"name" : "when",
"width" : 20
}
]
}, },
"form" : { "form" : {
"0" : { "0" : {
"TLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTL" : { "TLTLTLTL" : {
"mci" : { "mci" : {
"TL1" : { // :TODO: Bug: Without any keys here, theme customization does not apply!!!!
//"text" : "@method:getLastCaller" "TL1" : {
"styleSGR1" : "|00|24"
}, },
"TL2" : { "TL2" : {
//"text" : "@method:getLastCaller" "styleSGR1" : "|00|24"
},
"TL3" : {
"styleSGR1" : "|00|24"
},
"TL4" : {
"styleSGR1" : "|00|24"
} }
} }
} }
} }

Binary file not shown.

View File

@ -29,7 +29,7 @@
"focusTextStyle" : "l33t" "focusTextStyle" : "l33t"
} }
}, },
"apply" : { "apply" : {
"ET1" : { "width" : 21 }, "ET1" : { "width" : 21 },
"ET2" : { "width" : 21 }, "ET2" : { "width" : 21 },
//"ET3" : { "width" : 21 }, //"ET3" : { "width" : 21 },
@ -45,7 +45,24 @@
"ET8" : { "width" : 21 }, "ET8" : { "width" : 21 },
"ET9" : { "width" : 21 }, "ET9" : { "width" : 21 },
"ET10" : { "width" : 21 } "ET10" : { "width" : 21 }
} },
"lastCallers" : {
"TL1" : {
"resizable" : false,
"width" : 16,
"textOverflow" : "..."
},
"TL2" : {
"resizable" : false,
"width" : 19,
"textOverflow" : "..."
},
"TL3" : {
"resizable" : false,
"width" : 17,
"textOverflow" : "..."
}
}
}, },
"prompts" : { "prompts" : {
"userCredentials" : { "userCredentials" : {