* 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] },
};
// :TODO: These should use setPropertyValue()!
function setOption(pos, name) {
if(mci.args.length > pos && mci.args[pos].length > 0) {
options[name] = mci.args[pos];

View File

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

View File

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

View File

@ -67,7 +67,10 @@ function View(options) {
this.setDimension(options.dimens);
this.autoScale = { height : false, width : false };
} 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
@ -214,6 +217,12 @@ View.prototype.setPropertyValue = function(propName, value) {
*/
break;
case 'resizable' :
if(_.isBoolean(value)) {
this.resizable = value;
}
break;
case 'argName' : this.submitArgName = value; break;
}

View File

@ -4,6 +4,7 @@
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 util = require('util');
var moment = require('moment');
@ -13,17 +14,15 @@ var _ = require('lodash');
exports.moduleInfo = {
name : 'Last Callers',
desc : 'Last 10 callers to the system',
desc : 'Last callers to the system',
author : 'NuSkooler',
packageName : 'codes.l33t.enigma.lastcallers'
};
exports.getModule = LastCallersModule;
// :TODO:
// * Order should be menu/theme defined
// * Text needs overflow defined (optional), e.g. "..."
// * Date/time format should default to theme short date + short time
// *
// * config.evenRowSGR (optional)
function LastCallersModule(options) {
MenuModule.call(this, options);
@ -31,66 +30,29 @@ function LastCallersModule(options) {
var self = this;
this.menuConfig = options.menuConfig;
this.menuMethods = {
getLastCaller : function(formData, extraArgs) {
//console.log(self.lastCallers[self.lastCallerIndex])
var lc = self.lastCallers[self.lastCallerIndex++];
var when = moment(lc.timestamp).format(self.menuConfig.config.dateTimeFormat);
return util.format('%s %s %s %s', lc.name, lc.location, lc.affiliation, when);
this.rows = 10;
if(this.menuConfig.config) {
if(_.isNumber(this.menuConfig.config.rows)) {
this.rows = Math.max(1, this.menuConfig.config.rows);
}
if(_.isString(this.menuConfig.config.dateTimeFormat)) {
this.dateTimeFormat = this.menuConfig.config.dateTimeFormat;
}
}
};
}
util.inherits(LastCallersModule, MenuModule);
/*
LastCallersModule.prototype.enter = function(client) {
LastCallersModule.super_.prototype.enter.call(this, client);
var self = this;
self.lastCallers = [];
self.lastCallerIndex = 0;
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;
// we need the client to init this for theming
if(!_.isString(this.dateTimeFormat)) {
this.dateTimeFormat = this.client.currentTheme.helpers.getDateFormat('short') +
this.client.currentTheme.helpers.getTimeFormat('short');
}
}, 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.super_.prototype.mciReady.call(this, mciData);
@ -98,12 +60,7 @@ LastCallersModule.prototype.mciReady = function(mciData) {
var self = this;
var vc = self.viewControllers.lastCallers = new ViewController( { client : self.client } );
var lc = [];
var count = _.size(mciData.menu) / 4;
if(count < 1) {
// :TODO: Log me!
return;
}
var rows = self.rows;
async.series(
[
@ -118,12 +75,13 @@ 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 ' + count + ';',
'LIMIT ' + rows + ';',
function historyRow(err, histEntry) {
lc.push( {
userId : histEntry.user_id,
@ -132,7 +90,7 @@ LastCallersModule.prototype.mciReady = function(mciData) {
} );
},
function complete(err, recCount) {
count = recCount; // adjust to retrieved
rows = recCount; // adjust to retrieved
callback(err);
}
);
@ -156,124 +114,65 @@ LastCallersModule.prototype.mciReady = function(mciData) {
});
},
function createAndPopulateViews(callback) {
assert(lc.length === count);
var rowsPerColumn = count / 4;
//
// TL1...count = who
// TL<count>...<count*2> = location
// TL1 = who
// TL2 = location
// TL3 = affiliation
// TL4 = when
//
var i;
var v;
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);
}
// These form the order/layout for a row. Additional rows
// will use them as a template.
//
// 1..count/4 = who
// count/10
/*
var viewOpts = {
client : self.client,
var views = {
who : vc.getView(1),
location : vc.getView(2),
affils : vc.getView(3),
when : vc.getView(4),
};
var rowViewId = 1;
var v;
lc.forEach(function lcEntry(caller) {
v = vc.getView(rowViewId++);
var row = views.who.position.row;
self.menuConfig.config.fields.forEach(function field(f) {
switch(f.name) {
case 'who' :
var nextId = 5;
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.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));
}
});
v.setText(caller.who)
row++;
});
*/
}
],
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" : {
"argName" : "birthdate",
//"width" : 8,
"maskPattern" : "####/##/##"
},
"ET4" : {
"argName" : "sex",
//"width" : 1,
"maxLength" : 1
},
"ET5" : {
@ -212,37 +210,26 @@
"art" : "LASTCALL.ANS",
"options" : { "cls" : true },
"config" : {
"dateTimeFormat" : "ddd MMM Do H:mm a",
"fields" : [
{
"name" : "who",
"width" : 17
},
{
"name" : "location",
"width" : 20
},
{
"name" : "affiliation",
"width" : 20
},
{
"name" : "when",
"width" : 20
}
]
"dateTimeFormat" : "ddd MMM Do H:mm a"
},
"form" : {
"0" : {
"TLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTLTL" : {
"TLTLTLTL" : {
"mci" : {
// :TODO: Bug: Without any keys here, theme customization does not apply!!!!
"TL1" : {
//"text" : "@method:getLastCaller"
"styleSGR1" : "|00|24"
},
"TL2" : {
//"text" : "@method:getLastCaller"
"styleSGR1" : "|00|24"
},
"TL3" : {
"styleSGR1" : "|00|24"
},
"TL4" : {
"styleSGR1" : "|00|24"
}
}
}
}

Binary file not shown.

View File

@ -45,6 +45,23 @@
"ET8" : { "width" : 21 },
"ET9" : { "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" : {