Browse Source

render all login timespans to chart

master
poikilos 7 years ago
committed by Jacob Gustafson
parent
commit
e89d44e9fd
  1. 266
      webapp/server.js

266
webapp/server.js

@ -19,6 +19,7 @@ var app = express();
//app.set('view engine', 'handlebars'); //app.set('view engine', 'handlebars');
var players = []; var players = [];
var player_indices = {}; var player_indices = {};
var play_dates = [];
//see https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction //see https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction
var prev_line = null; var prev_line = null;
@ -41,7 +42,8 @@ var unique_flags = [
]; ];
//#endregion derived from mtsenliven.py //#endregion derived from mtsenliven.py
function process_logline(line, line_number, selected_date_s) { function process_logline(line, line_number) {
//selected_date_s
//TODO: use store_unique_log_data instead of this function //TODO: use store_unique_log_data instead of this function
var player_name = null; var player_name = null;
var verb = ""; var verb = "";
@ -72,7 +74,7 @@ function process_logline(line, line_number, selected_date_s) {
verb_number = uf_i; verb_number = uf_i;
verb = unique_flags[uf_i]; verb = unique_flags[uf_i];
date_s = line.substring(0,10).trim(); date_s = line.substring(0,10).trim();
if (selected_date_s==null || selected_date_s==date_s) { //if (selected_date_s==null || selected_date_s==date_s) {
//console.log("(verbose message in process_logline) using '" + date_s + "' since selected '"+selected_date_s+"'"); //console.log("(verbose message in process_logline) using '" + date_s + "' since selected '"+selected_date_s+"'");
time_s = line.substring(time_start_i, time_start_i+8); time_s = line.substring(time_start_i, time_start_i+8);
//console.log("using time "+time_s); //console.log("using time "+time_s);
@ -88,11 +90,11 @@ function process_logline(line, line_number, selected_date_s) {
else { else {
player_name = "<missing msgprefix&rt;"; player_name = "<missing msgprefix&rt;";
} }
} //}
else { //else {
skip_date_enable = true; // skip_date_enable = true;
//console.log("WARNING in process_logline: skipping '" + date_s + "' since not '"+selected_date_s+"'"); //console.log("WARNING in process_logline: skipping '" + date_s + "' since not '"+selected_date_s+"'");
} //}
break; break;
} }
} }
@ -116,18 +118,66 @@ function process_logline(line, line_number, selected_date_s) {
console.log("WARNING in process_logline: zero-length player name"); console.log("WARNING in process_logline: zero-length player name");
} }
} }
if (index<0 && !skip_date_enable) { if (index<0 && (verb=="leaves game"||verb=="joins game")) {
//console.log("(verbose message in process_logline) "+index_msg+"index was '"+index+"' but date was good '" + date_s + "' for '"+line+"'"); console.log("(ERROR in process_logline) " + index_msg +
"index was '"+index+"' but date was present '" +
date_s + "' for '"+line+"' (no player found, but" +
"verb is a player verb).");
} }
var play_date_enable = false;
if (verb == "leaves game") { if (verb == "leaves game") {
if (index > -1) { if (index > -1) {
players[index].logout_time = time_s; var play_i = -1;
if (!players[index].hasOwnProperty("plays")) {
players[index].plays = {};
}
if (!players[index].plays.hasOwnProperty(date_s)) {
//leave login time blank--player must have logged in before the available part of the log began
players[index].plays[date_s] = [];
players[index].plays[date_s].push({});
play_i = 0;
}
else {
if (players[index].plays[date_s].length==0) players[index].plays[date_s].push({});
play_i = players[index].plays[date_s].length - 1;
if (players[index].plays[date_s][play_i].hasOwnProperty("logout_time")) {
//If last entry is incomplete, start a new one:
players[index].plays[date_s].push({});
play_i++;
}
}
players[index].plays[date_s][play_i].logout_time = time_s;
play_date_enable = true;
} }
} }
else if (verb == "joins game") { else if (verb == "joins game") {
if (index > -1) { if (index > -1) {
//console.log("using index " + index); if (player_ip!=null) {
players[index].login_time = time_s; players[index].player_ip = player_ip;
var play_i = -1;
if (!players[index].hasOwnProperty("plays")) {
players[index].plays = {};
}
if (!players[index].plays.hasOwnProperty(date_s)) {
players[index].plays[date_s] = [];
play_i = 0;
}
else play_i = players[index].plays[date_s].length;
players[index].plays[date_s].push({});
//console.log(verb+" on "+date_s+" (length "+players[index].plays[date_s].length+") play "+play_i+"+1 for player ["+index+"] "+player_name+"...");
players[index].plays[date_s][play_i].login_time = time_s;
play_date_enable = true;
}
// else redundant (server writes " joins game " again
// and shows list of players instead of ip).
//TODO: else analyze list of players to confirm in case player logged in all day
}
}
if (play_date_enable) {
if (date_s.length>0) {
if (play_dates.indexOf(date_s) < 0) {
play_dates.push(date_s);
}
} }
} }
} }
@ -188,30 +238,12 @@ function store_unique_log_data(output, line_number, err_flag=false) {
} }
return ret; return ret;
} }
var cached_date = null;
app.get('/', function (req, res) { function read_log() {
var ret = ""; if (players==null) players = [];
ret += '<html><body style="font-family:calibri,sans">'; if (player_indices==null) player_indices = {};
// Whenever server starts the following is logged // os.homedir() + "/.minetest/debug_archived/2018/05/08.txt",
// (see also etc/example-input.txt): var log_paths = [os.homedir() + "/.minetest/debug.txt"];
//
//-------------
// Separator
//-------------
//
var selected_date_s = null;
if (req.query.date) selected_date_s = req.query.date
ret += "assuming minetestserver ran as: " + os.homedir() + "<br/>\n";
ret += "timezone (tz_offset/60*-1): " + (Math.floor(tz_offset/60)*-1) + "<br/>\n";
ret += "date (this YYYY-MM-DD filter limits all log processing): " + selected_date_s + "<br/>\n";
if (selected_date_s==null) {
ret += '<a href="?date=2018-05-08">2018-05-08</a>';
}
if (cached_date!=selected_date_s && selected_date_s!=null) {
cached_date = selected_date_s;
players = [];
player_indices = [];
var log_paths = [os.homedir() + "/.minetest/debug_archived/2018/05/08.txt", os.homedir() + "/.minetest/debug.txt"];
var lp_len = log_paths.length; var lp_len = log_paths.length;
for (var lp_i=0; lp_i<lp_len; lp_i++) { for (var lp_i=0; lp_i<lp_len; lp_i++) {
var this_log_path = log_paths[lp_i]; var this_log_path = log_paths[lp_i];
@ -224,7 +256,7 @@ app.get('/', function (req, res) {
while (next) { while (next) {
next = liner.next(); next = liner.next();
if (next!=false) { if (next!=false) {
process_logline(next.toString('ascii'), line_number, selected_date_s); process_logline(next.toString('ascii'), line_number);
line_number++; line_number++;
} }
} }
@ -233,17 +265,56 @@ app.get('/', function (req, res) {
console.log("WARNING: file not found: '" + this_log_path + "' (listing actual archived log folders is not yet implemented, so this is a hard-coded demo folder only on poikilos' server)"); console.log("WARNING: file not found: '" + this_log_path + "' (listing actual archived log folders is not yet implemented, so this is a hard-coded demo folder only on poikilos' server)");
} }
} }
}
} app.get('/get-players', function (req, res) {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(players));
});
app.get('/', function (req, res) {
var ret = "";
ret += '<html><body style="font-family:calibri,sans">';
// Whenever server starts the following is logged
// (see also etc/example-input.txt):
//
//-------------
// Separator
//-------------
//
var selected_date_s = null;
if (req.query.date) selected_date_s = req.query.date
ret += "assuming minetestserver ran as: " + os.homedir() + "<br/>\n";
ret += "timezone (tz_offset/60*-1): " + (Math.floor(tz_offset/60)*-1) + "<br/>\n";
ret += "date: " + selected_date_s + "<br/>\n";
//ret += 'var play_dates = [];';
var pdLength = 0;
if (play_dates != null) pdLength = play_dates.length;
for (var pd_i = 0; pd_i < pdLength; pd_i++) {
//ret += 'play_dates.push("' + play_dates[pd_i] + '");';
if (selected_date_s!=play_dates[pd_i]) {
ret += '<a href="?date='+play_dates[pd_i]+'">'+play_dates[pd_i]+'</a> ';
}
else {
ret += play_dates[pd_i]+' ';
}
}
//if (selected_date_s==null) {
//ret += '<a href="?date=2018-05-08">2018-05-08</a>';
//}
//see ~/.minetest/debug.txt //see ~/.minetest/debug.txt
//and logs archived by EnlivenMinetest: //and logs archived by EnlivenMinetest:
//~/.minetest/debug_archived/2018/05/08.txt //~/.minetest/debug_archived/2018/05/08.txt
ret += `<canvas id="myCanvas" width="100" height="1540"> <!--style="border:1px solid #c3c3c3;">--> ret += `
<div style="color:gray" id="statusArea"></div>
<canvas id="myCanvas" width="100" height="1540"> <!--style="border:1px solid #c3c3c3;">-->
Your browser does not support the canvas element. Your browser does not support the canvas element.
</canvas> </canvas>
<div style="color:gray" id="outputArea">loading...</div> <div style="color:gray" id="outputArea">loading...</div>
<script> <script>
`;
ret += 'var date_s = ' + (selected_date_s?('"'+selected_date_s+'"'):"null") + ';';
ret += `
var start_hour = 0; var start_hour = 0;
var start_m = start_hour*60; var start_m = start_hour*60;
var start_s = start_m*60; var start_s = start_m*60;
@ -267,26 +338,21 @@ var time_area_width = hour_width*hour_count;
var canvas_w = player_name_width + time_area_width; var canvas_w = player_name_width + time_area_width;
stats_html += '<div>' + hour_count + "hrs * " + hour_width + "px = " + time_area_width + '</div>' stats_html += '<div>' + hour_count + "hrs * " + hour_width + "px = " + time_area_width + '</div>'
stats_html += '<div>' + time_area_width + "px + " + player_name_width + "px player name = " + canvas_w + 'px canvas width</div>' stats_html += '<div>' + time_area_width + "px + " + player_name_width + "px player name = " + canvas_w + 'px canvas width</div>'
var outputE = document.getElementById("outputArea"); var outputElement = document.getElementById("outputArea");
outputE.innerHTML = stats_html;`; var statusElement = document.getElementById("statusArea");
statusElement.innerHTML="...";
function draw_players() {
var canvas = document.getElementById("myCanvas");
canvas.width = canvas_w;
var ctx = canvas.getContext("2d");
ctx.font = text_size+"px Arial";
var arrayLength = players.length; var arrayLength = players.length;
for (var i = 0; i < arrayLength; i++) { var text_x;
ret += 'players[' + i + '] = {};' + "\n"; var index = -1;`
ret += 'players[' + i + '].login_time = "' + players[i].login_time + '";' + "\n";
ret += 'players[' + i + '].logout_time = "' + players[i].logout_time + '";' + "\n";
ret += 'players[' + i + '].display_name = "' + players[i].display_name + '";' + "\n";
}
ret += `var canvas = document.getElementById("myCanvas");
canvas.width = canvas_w;
var ctx = canvas.getContext("2d");
ctx.font = text_size+"px Arial";
var arrayLength = players.length;
var text_x;
var index = -1;`
ret += 'var tz_offset = ' + tz_offset + ';'; ret += 'var tz_offset = ' + tz_offset + ';';
ret += `for (var i = 0; index < arrayLength; i++) { ret += `
for (var i = 0; index < arrayLength; i++) {
ctx.font = text_size+"px Arial";
text_x = text_start_x; text_x = text_start_x;
ctx.fillStyle = "#dce5ea"; ctx.fillStyle = "#dce5ea";
ctx.fillRect(0, border_y+1.0, player_name_width, row_y_offset-1.0); ctx.fillRect(0, border_y+1.0, player_name_width, row_y_offset-1.0);
@ -318,11 +384,28 @@ var index = -1;`
else { else {
ctx.fillText(players[index].display_name, text_x, text_y); ctx.fillText(players[index].display_name, text_x, text_y);
var border_x = border_start_x; var border_x = border_start_x;
var login_places = players[index].login_time.split(':'); var playsLength = 0;
var plays = null;
if (date_s!=null) {
if (players[index].plays != undefined) {
if (players[index].plays.hasOwnProperty(date_s)) {
plays = players[index].plays[date_s];
playsLength = plays.length;
}
}
}
for (var s_i = 0; s_i < playsLength; s_i++) {
if (!plays[s_i].hasOwnProperty("login_time")) {
plays[s_i].login_time = "00:00:00";
}
var login_places = plays[s_i].login_time.split(':');
if (login_places.length>=3) { if (login_places.length>=3) {
login_places[0] -= Math.floor(tz_offset/60); login_places[0] -= Math.floor(tz_offset/60);
} }
var logout_places = players[index].logout_time.split(':'); if (!plays[s_i].hasOwnProperty("logout_time")) {
plays[s_i].logout_time = "23:59:59";
}
var logout_places = plays[s_i].logout_time.split(':');
if (logout_places.length>=3) { if (logout_places.length>=3) {
logout_places[0] -= Math.floor(tz_offset/60); logout_places[0] -= Math.floor(tz_offset/60);
} }
@ -335,19 +418,78 @@ var index = -1;`
if (right_x<left_x) { if (right_x<left_x) {
right_x = canvas_w; //not logged out yet, so end of bar is at end right_x = canvas_w; //not logged out yet, so end of bar is at end
} }
ctx.fillStyle = "#FF0000"; ctx.fillStyle = "#008000";
ctx.fillRect(left_x, border_y+(row_y_offset/3.0), right_x-left_x, row_y_offset/3.0); ctx.fillRect(left_x, border_y+(row_y_offset/3.0), right_x-left_x, row_y_offset/3.0);
ctx.font = (text_size*.66)+"px Arial";
ctx.fillStyle = "#000000"; ctx.fillStyle = "#000000";
ctx.fillText(login_places[0]+":"+login_places[1], left_x, text_y-(row_y_offset/3.0)); ctx.fillText(login_places[0]+":"+login_places[1], left_x, text_y-(row_y_offset/3.0));
ctx.fillStyle = "#808080"; ctx.fillStyle = "#808080";
ctx.fillText(logout_places[0]+":"+logout_places[1], right_x, text_y+(row_y_offset/3.0)); var logout_s = logout_places[0]+":"+logout_places[1];
ctx.fillText(logout_s, right_x-ctx.measureText(logout_s).width, text_y+(row_y_offset/3.0));
}
} }
ctx.stroke(); ctx.stroke();
text_y += row_y_offset; text_y += row_y_offset;
border_y += row_y_offset; border_y += row_y_offset;
index++; index++;
}
} }
</script>`; outputElement.innerHTML = stats_html;`;
//NOTE: window.location.href is the entire address including query params!
ret += `
var request = new XMLHttpRequest();
var getUrl = window.location;
var request_href = getUrl.protocol + "//" + getUrl.host + "/"+'get-players';
statusElement.innerHTML = request_href+"...";
request.open('GET', request_href, true);
request.onload = function() {
if (this.status >= 200 && this.status < 400) {
// Success!
statusElement.innerHTML = "loaded player(s)";
if (!statusElement.classList.contains('alert') ) {
statusElement.classList.add('alert');
}
try {
players = JSON.parse(this.response);
statusElement.classList.add('alert-success');
statusElement.innerHTML = "loaded " + players.length + ' player(s) <span style="color:white">from "'+this.responseURL+'"</span>';
draw_players();
}
catch(e) {
statusElement.classList.add('alert-warning');
statusElement.innerHTML = e + " for URL '"+request_href+"'";
//alert(this.response);
}
} else {
// We reached our target server, but it returned an error
statusElement.innerHTML = "Error " + this.status + " accessing " + request_href;
}
};
request.onerror = function() {
// There was a connection error of some sort
};
request.send();
`;
/*
var arrayLength = players.length;
for (var i = 0; i < arrayLength; i++) {
ret += 'players[' + i + '] = {};' + "\n";
if (players.plays!=undefined) {
ret += 'players[' + i + '].plays = {};' + "\n";
//var playsLength = players.plays.length;
for (var s_i = 0; s_i < playsLength; s_i++) {
ret += 'players[' + i + '].plays[' + s_i + '].login_time = "' + players[i].plays[s_i].login_time + '";' + "\n";
ret += 'players[' + i + '].plays[' + s_i + '].logout_time = "' + players[i].plays[s_i].logout_time + '";' + "\n";
}
}
ret += 'players[' + i + '].display_name = "' + players[i].display_name + '";' + "\n";
}
*/
ret += `</script>`;
ret += '</body></html>'; ret += '</body></html>';
res.send(ret); res.send(ret);
//res.render('home'); //res.render('home');
@ -359,5 +501,7 @@ var server = app.listen(3000, function () {
//console.log('express-handlebars example server listening on: 3000'); //console.log('express-handlebars example server listening on: 3000');
var host = server.address().address; var host = server.address().address;
var port = server.address().port; var port = server.address().port;
console.log("reading log...");
read_log();
console.log("EnlivenMinetest webapp listening at http://%s:%s", host, port); console.log("EnlivenMinetest webapp listening at http://%s:%s", host, port);
}); });

Loading…
Cancel
Save