TONS of work.
* Got stats page working * Got main LevelUp page working * A lot of stat processing stuff
This commit is contained in:
parent
083f9f3901
commit
278d409ad9
9
assets/css/pure-min.css
vendored
9
assets/css/pure-min.css
vendored
File diff suppressed because one or more lines are too long
@ -211,6 +211,60 @@ aside {
|
|||||||
background: #1f8dd6;
|
background: #1f8dd6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bordered {
|
||||||
|
border: 1px solid #888;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.levelup-user {
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #888;
|
||||||
|
margin: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 75px;
|
||||||
|
background: #AAA;
|
||||||
|
padding: 5px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.levelup-username {
|
||||||
|
font-size: 2em;
|
||||||
|
color: #FFF;
|
||||||
|
background: #777;
|
||||||
|
border-radius: 3px;
|
||||||
|
border-top: 1px solid black;
|
||||||
|
border-left: 1px solid black;
|
||||||
|
border-right: 1px solid white;
|
||||||
|
border-bottom: 1px solid white;
|
||||||
|
padding: 5px;
|
||||||
|
position: absolute;
|
||||||
|
top: 5%;
|
||||||
|
right: 1%;
|
||||||
|
}
|
||||||
|
.levelup-level {
|
||||||
|
font-size: 7em;
|
||||||
|
margin-right: 5em;
|
||||||
|
color: #000;
|
||||||
|
position: absolute;
|
||||||
|
left: 1%;
|
||||||
|
top: 35%;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
.levelup-smalllevel {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20%;
|
||||||
|
right: 1%;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.levelup-xp {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 1%;
|
||||||
|
right: 1%;
|
||||||
|
}
|
||||||
/* -- Responsive Styles (Media Queries) ------------------------------------- */
|
/* -- Responsive Styles (Media Queries) ------------------------------------- */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
276
assets/js/B.js
Normal file
276
assets/js/B.js
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
function B(els, attrs) {
|
||||||
|
// Turn 'this' into an array of passed in elements.
|
||||||
|
function B(els) {
|
||||||
|
if(typeof els === "string") {
|
||||||
|
els = this.brb.create(els);
|
||||||
|
}
|
||||||
|
for(var i = 0; i < els.length; i++) {
|
||||||
|
this[i] = els[i];
|
||||||
|
}
|
||||||
|
this.length = els.length;
|
||||||
|
// return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map a function to all elements in 'this'
|
||||||
|
B.prototype.map = function(callback) {
|
||||||
|
var results = [], i = 0;
|
||||||
|
for(;i<this.length;i++){
|
||||||
|
results.push(callback.call(this,this[i],i));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Foreach through all elements in 'this'
|
||||||
|
B.prototype.forEach = function(callback) {
|
||||||
|
this.map(callback);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map a function to the first element in 'this'
|
||||||
|
B.prototype.mapOne = function(callback) {
|
||||||
|
var m = this.map(callback);
|
||||||
|
return m.length > 1 ? m[0] : m;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update css for each element in 'this'
|
||||||
|
B.prototype.css = function(css_opt_var, css_opt_val) {
|
||||||
|
if(typeof css_opt_var !== "string") {
|
||||||
|
for(css_var in css_opt_var) {
|
||||||
|
this.forEach(function(el){el.style[css_var]=css_opt_var[css_var];});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
if(typeof css_opt_val !== "undefined") {
|
||||||
|
return this.forEach(function(el){el.style[css_opt_var]=css_opt_val;});
|
||||||
|
} else {
|
||||||
|
return this.mapOne(function(el){return el.style[css_opt_var];});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update the innerText for each element in 'this'
|
||||||
|
B.prototype.html = function(text) {
|
||||||
|
if(typeof text !== "undefined") {
|
||||||
|
return this.forEach(function(el){el.innerHTML=text;});
|
||||||
|
} else {
|
||||||
|
return this.mapOne(function(el){return el.innerHTML;});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if the first element has the requested class
|
||||||
|
B.prototype.hasClass = function(cls) {
|
||||||
|
return this.mapOne(function(el){
|
||||||
|
return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a class to each element in 'this'
|
||||||
|
B.prototype.addClass = function(classes) {
|
||||||
|
var className = "";
|
||||||
|
if(typeof classes !== "string") {
|
||||||
|
for(var i=0;i<classes.length;i++) {
|
||||||
|
className+=" "+classes[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
className=" "+classes;
|
||||||
|
}
|
||||||
|
return this.forEach(function(el){el.className+=className;});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove a class from each element in 'this'
|
||||||
|
B.prototype.removeClass = function(remove_class) {
|
||||||
|
return this.forEach(function(el){
|
||||||
|
var cs = el.className.split(" "), i;
|
||||||
|
while((i=cs.indexOf(remove_class))>-1){
|
||||||
|
cs = cs.slice(0,i).concat(cs.slice(++i));
|
||||||
|
}
|
||||||
|
el.className=cs.join(" ");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set an attribute for each element in 'this'
|
||||||
|
B.prototype.attr = function(attr,val){
|
||||||
|
if(typeof val!=="undefined"){
|
||||||
|
if(this[0].tagName=="INPUT" && attr.toUpperCase()=="VALUE") {
|
||||||
|
// If we're setting the 'VALUE' then it's actually .value
|
||||||
|
return this.forEach(function(el){
|
||||||
|
el.value=val;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Otherwise use .setAttribute
|
||||||
|
return this.forEach(function(el){
|
||||||
|
el.setAttribute(attr,val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// And getting the value
|
||||||
|
if(this[0].tagName=="INPUT" && attr.toUpperCase()=="VALUE") {
|
||||||
|
return this.mapOne(function(el){
|
||||||
|
return el.value;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return this.mapOne(function(el){
|
||||||
|
return el.getAttribute(attr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Actually set a value on each element (can be done with attr too.)
|
||||||
|
B.prototype.val = function(new_val) {
|
||||||
|
if(typeof new_val!=="undefined"){
|
||||||
|
return this.forEach(function(el){
|
||||||
|
el.value = new_val;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Just retrieve the value for the first element
|
||||||
|
return this.mapOne(function(el) {
|
||||||
|
return el.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append an element to the DOM after each element in 'this'
|
||||||
|
B.prototype.append = function(els) {
|
||||||
|
this.forEach(function(parEl, i) {
|
||||||
|
els.forEach(function(childEl) {
|
||||||
|
if(i>0) {
|
||||||
|
childEl=childEl.cloneNode(true);
|
||||||
|
}
|
||||||
|
parEl.appendChild(childEl);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepend an element to the DOM before each element in 'this'
|
||||||
|
B.prototype.prepend = function(els) {
|
||||||
|
return this.forEach(function(parEl, i) {
|
||||||
|
for(var j = els.length-1; j>-1; j--) {
|
||||||
|
childEl=(i>0)?els[j].cloneNode(true):els[j];
|
||||||
|
parEl.insertBefore(childEl, parEl.firstChild);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove all elements in 'this' from the DOM
|
||||||
|
B.prototype.remove = function() {
|
||||||
|
return this.forEach(function(el){
|
||||||
|
return el.parentNode.removeChild(el);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find children that match selector
|
||||||
|
B.prototype.children = function(selector) {
|
||||||
|
var results = [];
|
||||||
|
this.forEach(function(el) {
|
||||||
|
var sub_r = el.querySelectorAll(selector);
|
||||||
|
for(var i = 0; i < sub_r.length; i++) {
|
||||||
|
results.push(sub_r[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
B.prototype.firstChild = function(selector) {
|
||||||
|
return this.children(selector)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an event listener to each element in 'this'
|
||||||
|
B.prototype.on = (function(){
|
||||||
|
// Browser compatibility...
|
||||||
|
if(document.addEventListener) {
|
||||||
|
return function(evt,fn) {
|
||||||
|
return this.forEach(function(el){
|
||||||
|
el.addEventListener(evt, fn, false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
} else if(document.attachEvent) {
|
||||||
|
return function(evt,fn) {
|
||||||
|
return this.forEach(function(el){
|
||||||
|
el.attachEvent("on"+evt,fn);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return function(evt, fn) {
|
||||||
|
return this.forEach(function(el){
|
||||||
|
el["on"+evt]=fn;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
||||||
|
// Disable event listeners on elements in 'this'
|
||||||
|
B.prototype.off = (function(){
|
||||||
|
// Browser compatibility...
|
||||||
|
if(document.removeEventListener) {
|
||||||
|
return function(evt, fn) {
|
||||||
|
return this.forEach(function(el) {
|
||||||
|
el.removeEventListener(evt, fn, false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
} else if(document.detachEvent) {
|
||||||
|
return function(evt, fn) {
|
||||||
|
return this.forEach(function(el) {
|
||||||
|
el.detachEvent("on"+evt, fn);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return function(evt, fn) {
|
||||||
|
return this.forEach(function(el){
|
||||||
|
el["on"+evt]=null;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
||||||
|
// The actual framework object, yay!
|
||||||
|
var brb = {
|
||||||
|
// Get an element
|
||||||
|
get: function(selector) {
|
||||||
|
var els;
|
||||||
|
if(typeof selector === "string") {
|
||||||
|
els = document.querySelectorAll(selector);
|
||||||
|
} else if(selector.length) {
|
||||||
|
els = selector;
|
||||||
|
} else {
|
||||||
|
els = [selector];
|
||||||
|
}
|
||||||
|
return new B(els);
|
||||||
|
},
|
||||||
|
// Create a new element
|
||||||
|
create: function(tagName, attrs) {
|
||||||
|
var el = new B([document.createElement(tagName)]);
|
||||||
|
// Set attributes on new element
|
||||||
|
if(attrs) {
|
||||||
|
if(attrs.className) {
|
||||||
|
// Classes
|
||||||
|
el.addClass(attrs.className);
|
||||||
|
delete attrs.classname;
|
||||||
|
}
|
||||||
|
if(attrs.text) {
|
||||||
|
// Text
|
||||||
|
el.text(attrs.text);
|
||||||
|
delete attrs.text;
|
||||||
|
}
|
||||||
|
for(var key in attrs) {
|
||||||
|
// All other Attributes
|
||||||
|
if(attrs.hasOwnProperty(key)) {
|
||||||
|
el.attr(key, attrs[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if(els.match) {
|
||||||
|
var match_tags = els.match(/<([^>\s\/]*)\s?\/?>/);
|
||||||
|
}
|
||||||
|
if(match_tags && match_tags.length > 0) {
|
||||||
|
// It's a 'create tag' command
|
||||||
|
return brb.create(match_tags[1], attrs);
|
||||||
|
} else {
|
||||||
|
// Just search for matches
|
||||||
|
return brb.get(els);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// And there is our minimalist framework. Who needs jquery?
|
101
assets/js/levelup_main.js
Normal file
101
assets/js/levelup_main.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
document.addEventListener("DOMContentLoaded", function(event) {
|
||||||
|
sortByAlpha();
|
||||||
|
buildList();
|
||||||
|
B('#btnAlphaSort').on('click', function() {
|
||||||
|
if(sortMode == "ALPHA") {
|
||||||
|
sortMode = "ALPHA-REV";
|
||||||
|
B('#btnAlphaSort>i').removeClass('fa-sort-asc').addClass('fa-sort-desc');
|
||||||
|
} else {
|
||||||
|
sortMode = "ALPHA";
|
||||||
|
B('#btnAlphaSort>i').removeClass('fa-sort-desc').addClass('fa-sort-asc');
|
||||||
|
}
|
||||||
|
sortUsers();
|
||||||
|
buildList();
|
||||||
|
});
|
||||||
|
B('#btnXpSort').on('click', function() {
|
||||||
|
if(sortMode == "XP") {
|
||||||
|
sortMode = "XP-REV";
|
||||||
|
B('#btnXpSort>i').removeClass('fa-sort-desc').addClass('fa-sort-asc');
|
||||||
|
} else {
|
||||||
|
sortMode = "XP";
|
||||||
|
B('#btnXpSort>i').removeClass('fa-sort-asc').addClass('fa-sort-desc');
|
||||||
|
}
|
||||||
|
sortUsers();
|
||||||
|
buildList();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
var sortMode = "ALPHA";
|
||||||
|
|
||||||
|
function clearList() {
|
||||||
|
B(".levelup-user").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortUsers() {
|
||||||
|
switch(sortMode) {
|
||||||
|
case "ALPHA-REV": sortByAlphaRev(); break;
|
||||||
|
case "XP": sortByXP(); break;
|
||||||
|
case "XP-REV": sortByXPRev(); break;
|
||||||
|
default: sortByAlpha(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortByXP() {
|
||||||
|
levelUpStats.users.sort(function(a, b){
|
||||||
|
if(a.xp > b.xp) { return -1; }
|
||||||
|
if(a.xp < b.xp) { return 1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function sortByXPRev() {
|
||||||
|
levelUpStats.users.sort(function(a, b){
|
||||||
|
if(a.xp > b.xp) { return 1; }
|
||||||
|
if(a.xp < b.xp) { return -1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function sortByAlpha() {
|
||||||
|
levelUpStats.users.sort(function(a, b){
|
||||||
|
if(a.name > b.name) { return 1; }
|
||||||
|
if(a.name < b.name) { return -1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function sortByAlphaRev() {
|
||||||
|
levelUpStats.users.sort(function(a, b){
|
||||||
|
if(a.name > b.name) { return -1; }
|
||||||
|
if(a.name < b.name) { return 1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildList() {
|
||||||
|
clearList();
|
||||||
|
for(var i = 0; i < levelUpStats.users.length; i++) {
|
||||||
|
// Figure out the users current level & percentage through it.
|
||||||
|
var xp = levelUpStats.users[i].xp,
|
||||||
|
toNext = 100,
|
||||||
|
tstLvl = 1;
|
||||||
|
|
||||||
|
while(xp - toNext > 0) {
|
||||||
|
xp -= toNext;
|
||||||
|
tstLvl++;
|
||||||
|
toNext = tstLvl*100;
|
||||||
|
}
|
||||||
|
var donePct = (xp / toNext)*100,
|
||||||
|
userLevel = tstLvl,
|
||||||
|
userName = levelUpStats.users[i].name,
|
||||||
|
idName = userName.replace('.','_');
|
||||||
|
|
||||||
|
B("#userXpList").append(B('<div>').attr('id',"levelup-user-"+idName).addClass('levelup-user pure-u-1 pure-u-md-1-3').attr('data-username',userName));
|
||||||
|
var userNameSpan = B("<span>").attr('id','levelup-user-'+idName+'-name').addClass('levelup-username');
|
||||||
|
B("#levelup-user-"+idName).append(B("<div>").attr('id','levelup-user-'+idName+'-level').addClass('levelup-level').html(userLevel));
|
||||||
|
B("#levelup-user-"+idName).append(userNameSpan.html(userName));
|
||||||
|
B("#levelup-user-"+idName).append(B("<div>").attr('id','levelup-user-'+idName+'-smalllevel').addClass('levelup-smalllevel').html("Level "+userLevel));
|
||||||
|
B("#levelup-user-"+idName).append(B("<div>").attr('id','levelup-user-'+idName+'-xp').addClass('levelup-xp').html("("+xp+"/"+toNext+")"));
|
||||||
|
|
||||||
|
B('.levelup-user').on('click', function() {
|
||||||
|
console.log("Load User Profile: "+B(this).attr('data-username'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,45 @@
|
|||||||
document.addEventListener("DOMContentLoaded", function(event) {
|
document.addEventListener("DOMContentLoaded", function(event) {
|
||||||
|
B("#btnChannelStats").on('click', function(e) {
|
||||||
|
B(this).addClass('pure-button-disabled');
|
||||||
|
B('#btnMessageStats').removeClass('pure-button-disabled');
|
||||||
|
B('#btnUserStats').removeClass('pure-button-disabled');
|
||||||
|
B('#channelStats').removeClass('hidden');
|
||||||
|
B('#userStats').addClass('hidden');
|
||||||
|
B('#messageStats').addClass('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
B('#btnMessageStats').on('click', function(e) {
|
||||||
|
B(this).addClass('pure-button-disabled');
|
||||||
|
B('#btnChannelStats').removeClass('pure-button-disabled');
|
||||||
|
B('#channelStats').addClass('hidden');
|
||||||
|
B('#btnUserStats').removeClass('pure-button-disabled');
|
||||||
|
B('#userStats').addClass('hidden');
|
||||||
|
B('#messageStats').removeClass('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
B('#btnUserStats').on('click', function(e) {
|
||||||
|
B(this).addClass('pure-button-disabled');
|
||||||
|
B('#btnChannelStats').removeClass('pure-button-disabled');
|
||||||
|
B('#channelStats').addClass('hidden');
|
||||||
|
B('#btnMessageStats').removeClass('pure-button-disabled');
|
||||||
|
B('#messageStats').addClass('hidden');
|
||||||
|
B('#userStats').removeClass('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
B('#btnUsrHrlyStats').on('click', function(e) {
|
||||||
|
B(this).addClass('pure-button-disabled');
|
||||||
|
B('#btnUsrDowStats').removeClass('pure-button-disabled');
|
||||||
|
B('#userHourlyStats').removeClass('hidden');
|
||||||
|
B('#userDowStats').addClass('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
B('#btnUsrDowStats').on('click', function(e) {
|
||||||
|
B(this).addClass('pure-button-disabled');
|
||||||
|
B('#btnUsrHrlyStats').removeClass('pure-button-disabled');
|
||||||
|
B('#userHourlyStats').addClass('hidden');
|
||||||
|
B('#userDowStats').removeClass('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
var main_channel_names = ['general','random'],
|
var main_channel_names = ['general','random'],
|
||||||
main_member_numbers = [],
|
main_member_numbers = [],
|
||||||
main_message_numbers = [],
|
main_message_numbers = [],
|
||||||
@ -101,13 +142,212 @@ document.addEventListener("DOMContentLoaded", function(event) {
|
|||||||
name: 'Messages',
|
name: 'Messages',
|
||||||
color: 'rgba(126,86,134,0.9)',
|
color: 'rgba(126,86,134,0.9)',
|
||||||
data: other_message_numbers,
|
data: other_message_numbers,
|
||||||
pointPadding: 0.3
|
pointPadding: 0.0
|
||||||
},{
|
},{
|
||||||
name: 'Members',
|
name: 'Members',
|
||||||
color: 'rgba(165,170,217,1)',
|
color: 'rgba(165,170,217,1)',
|
||||||
data: other_member_numbers,
|
data: other_member_numbers,
|
||||||
pointPadding: 0.4,
|
pointPadding: 0.2,
|
||||||
yAxis: 1
|
yAxis: 1
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var msg1 = [{name: 'Messages', data: []}];
|
||||||
|
for(var i = 0; i < 24; i++) {
|
||||||
|
msg1[0].data.push(stats.messages.hours[i]);
|
||||||
|
}
|
||||||
|
var msgHourlyChart = new Highcharts.Chart({
|
||||||
|
chart: {
|
||||||
|
renderTo: 'msgHourlyStats'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: 'Hourly Stats'
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
categories: [
|
||||||
|
'00:00', '01:00', '02:00', '03:00', '04:00', '05:00',
|
||||||
|
'06:00', '07:00', '08:00', '09:00', '10:00', '11:00',
|
||||||
|
'12:00', '13:00', '14:00', '15:00', '16:00', '17:00',
|
||||||
|
'18:00', '19:00', '20:00', '21:00', '22:00', '23:00'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
title: { text: '' },
|
||||||
|
plotLines: [{
|
||||||
|
value: 0,
|
||||||
|
width: 1,
|
||||||
|
color: '#808080'
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
series: msg1
|
||||||
|
});
|
||||||
|
|
||||||
|
var msg2 = [{name: 'Messages', data: []}];
|
||||||
|
msg2[0].data.push(stats.messages.dow['Sun']);
|
||||||
|
msg2[0].data.push(stats.messages.dow['Mon']);
|
||||||
|
msg2[0].data.push(stats.messages.dow['Tue']);
|
||||||
|
msg2[0].data.push(stats.messages.dow['Wed']);
|
||||||
|
msg2[0].data.push(stats.messages.dow['Thu']);
|
||||||
|
msg2[0].data.push(stats.messages.dow['Fri']);
|
||||||
|
msg2[0].data.push(stats.messages.dow['Sat']);
|
||||||
|
|
||||||
|
var msgDowChart = new Highcharts.Chart({
|
||||||
|
chart: {
|
||||||
|
renderTo: 'msgDowStats'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: 'Day of Week Stats'
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
categories: [
|
||||||
|
'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
title: { text: '' },
|
||||||
|
plotLines: [{
|
||||||
|
value: 0,
|
||||||
|
width: 1,
|
||||||
|
color: '#808080'
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
series: msg2
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var numCharts = 0,
|
||||||
|
usrHourlyCharts = [],
|
||||||
|
usrDowCharts = [],
|
||||||
|
maxMessages = 0;
|
||||||
|
stats.users.sort(function(a, b){
|
||||||
|
if(a.name < b.name) { return -1; }
|
||||||
|
if(a.name > b.name) { return 1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
for(var i = 0; i < stats.users.length; i++) {
|
||||||
|
for(var j = 0; j < 24; j++) {
|
||||||
|
if(stats.users[i].messages.hours[j] > maxMessages) {
|
||||||
|
maxMessages = stats.users[i].messages.hours[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while((numCharts*10) < stats.users.length) {
|
||||||
|
var usrList = [],
|
||||||
|
usrHourlyActivity = [],
|
||||||
|
usrDowActivity = [];
|
||||||
|
for(var i = (numCharts*10); i < 10+(numCharts*10); i++) {
|
||||||
|
if(i < stats.users.length) {
|
||||||
|
usrList.push(stats.users[i].name);
|
||||||
|
for(var j = 0; j < 24; j++) {
|
||||||
|
usrHourlyActivity.push({
|
||||||
|
x: j,
|
||||||
|
y: (i - (numCharts*10)),
|
||||||
|
value: stats.users[i].messages.hours[j]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
dowArr=['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
|
||||||
|
for(var dowI = 0; dowI < 7; dowI++) {
|
||||||
|
usrDowActivity.push({
|
||||||
|
x: dowI,
|
||||||
|
y: (i - (numCharts*10)),
|
||||||
|
value: stats.users[i].messages.dow[dowArr[dowI]]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
B("#userHourlyStats").append(B("<div>").attr('id','usrHourlyStats-'+numCharts));
|
||||||
|
B("#userDowStats").append(B("<div>").attr('id','usrDowStats-'+numCharts));
|
||||||
|
|
||||||
|
usrHourlyCharts.push(new Highcharts.Chart({
|
||||||
|
chart: {
|
||||||
|
type: 'heatmap',
|
||||||
|
renderTo: 'usrHourlyStats-'+numCharts
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: 'Hourly Activity by User'
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
categories: [
|
||||||
|
'00:00','01:00','02:00','03:00','04:00','05:00',
|
||||||
|
'06:00','07:00','08:00','09:00','10:00','11:00',
|
||||||
|
'12:00','13:00','14:00','15:00','16:00','17:00',
|
||||||
|
'18:00','19:00','20:00','21:00','22:00','23:00'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
categories: usrList,
|
||||||
|
title: null
|
||||||
|
},
|
||||||
|
colorAxis: {
|
||||||
|
stops: [
|
||||||
|
[0, '#3060cf'],
|
||||||
|
[0.5, '#fffbbc'],
|
||||||
|
[0.9, '#c4463a']
|
||||||
|
],
|
||||||
|
min: 0,
|
||||||
|
max: maxMessages
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
borderWidth: 1,
|
||||||
|
data: usrHourlyActivity,
|
||||||
|
name: "Messages",
|
||||||
|
tooltip: {
|
||||||
|
pointFormatter: function() {
|
||||||
|
if(this.x < 10) {
|
||||||
|
return this.series.yAxis.categories[this.y] + " - 0"+this.x+":00 ("+this.value+" messages)";
|
||||||
|
}
|
||||||
|
return this.series.yAxis.categories[this.y] + " - "+this.x+":00 ("+this.value+" messages)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}));
|
||||||
|
|
||||||
|
usrDowCharts.push(new Highcharts.Chart({
|
||||||
|
chart: {
|
||||||
|
type: 'heatmap',
|
||||||
|
renderTo: 'usrDowStats-'+numCharts
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: 'Day Of Week Activity by User'
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
categories: [
|
||||||
|
'Sun','Mon','Tue','Wed','Thu','Fri','Sat'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
categories: usrList,
|
||||||
|
title: null
|
||||||
|
},
|
||||||
|
colorAxis: {
|
||||||
|
stops: [
|
||||||
|
[0, '#3060cf'],
|
||||||
|
[0.5, '#fffbbc'],
|
||||||
|
[0.9, '#c4463a']
|
||||||
|
],
|
||||||
|
min: 0,
|
||||||
|
max: maxMessages
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
borderWidth: 1,
|
||||||
|
data: usrDowActivity,
|
||||||
|
name: "Messages",
|
||||||
|
tooltip: {
|
||||||
|
pointFormatter: function() {
|
||||||
|
if(this.x < 10) {
|
||||||
|
return this.series.yAxis.categories[this.y] + " - 0"+this.x+":00 ("+this.value+" messages)";
|
||||||
|
}
|
||||||
|
return this.series.yAxis.categories[this.y] + " - "+this.x+":00 ("+this.value+" messages)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}));
|
||||||
|
numCharts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
B('#userStats').addClass('hidden');
|
||||||
|
B('#messageStats').addClass('hidden');
|
||||||
|
B('#userDowStats').addClass('hidden');
|
||||||
});
|
});
|
||||||
|
40
levelup_model.go
Normal file
40
levelup_model.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/boltdb/bolt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getAllLevelUpChannelXp(user string) map[string]int {
|
||||||
|
openDatabase()
|
||||||
|
ret := make(map[string]int)
|
||||||
|
// First, get a list of all levelup stats
|
||||||
|
db.Update(func(tx *bolt.Tx) error {
|
||||||
|
var b, uB, uSB *bolt.Bucket
|
||||||
|
var err error
|
||||||
|
|
||||||
|
b = tx.Bucket([]byte("users"))
|
||||||
|
if b == nil {
|
||||||
|
return fmt.Errorf("Unable to open 'users' bucket")
|
||||||
|
}
|
||||||
|
if uB = b.Bucket([]byte(user)); uB != nil {
|
||||||
|
if uSB = uB.Bucket([]byte("stats")); uSB != nil {
|
||||||
|
return uSB.ForEach(func(k, v []byte) error {
|
||||||
|
if strings.HasPrefix(string(k), "levelup-") {
|
||||||
|
mapKey := strings.Replace(string(k), "levelup-", "", -1)
|
||||||
|
if mapKey != "xp" {
|
||||||
|
ret[mapKey], _ = strconv.Atoi(string(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
closeDatabase()
|
||||||
|
return ret
|
||||||
|
}
|
@ -88,31 +88,54 @@ func (p *generalStatProcessor) GetName() string {
|
|||||||
func (p *generalStatProcessor) GetStatKeys() []string {
|
func (p *generalStatProcessor) GetStatKeys() []string {
|
||||||
return []string{
|
return []string{
|
||||||
"bot-message",
|
"bot-message",
|
||||||
|
//"bot-reaction-*",
|
||||||
"channel-message",
|
"channel-message",
|
||||||
|
//"channel-reaction-*",
|
||||||
"message-hour-*",
|
"message-hour-*",
|
||||||
"message-dow-*",
|
"message-dow-*",
|
||||||
"message-dom-*",
|
"message-dom-*",
|
||||||
|
//"reaction-*-hour-*",
|
||||||
|
//"reaction-*-dow-*",
|
||||||
|
//"reaction-*-dom-*",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *generalStatProcessor) ProcessMessage(m *Message) {
|
func (p *generalStatProcessor) ProcessMessage(m *Message) {
|
||||||
|
if m.Type == "message" {
|
||||||
incrementUserStat(m.User, "message-hour-"+m.Time.Format("15"))
|
incrementUserStat(m.User, "message-hour-"+m.Time.Format("15"))
|
||||||
incrementUserStat(m.User, "message-dow-"+m.Time.Format("Mon"))
|
incrementUserStat(m.User, "message-dow-"+m.Time.Format("Mon"))
|
||||||
incrementUserStat(m.User, "message-dom-"+m.Time.Format("02"))
|
incrementUserStat(m.User, "message-dom-"+m.Time.Format("02"))
|
||||||
|
//} else if m.Type == "reaction_added" {
|
||||||
|
//incrementUserStat(m.User, "reaction-"+m.Name+"-hour-"+m.Time.Format("15"))
|
||||||
|
//incrementUserStat(m.User, "reaction-"+m.Name+"-dow-"+m.Time.Format("Mon"))
|
||||||
|
//incrementUserStat(m.User, "reaction-"+m.Name+"-dom-"+m.Time.Format("02"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
func (p *generalStatProcessor) ProcessBotMessage(m *Message) {}
|
func (p *generalStatProcessor) ProcessBotMessage(m *Message) {}
|
||||||
|
|
||||||
func (p *generalStatProcessor) ProcessUserMessage(m *Message) {
|
func (p *generalStatProcessor) ProcessUserMessage(m *Message) {
|
||||||
|
if m.Type == "message" {
|
||||||
incrementUserStat(m.User, "bot-message")
|
incrementUserStat(m.User, "bot-message")
|
||||||
|
//} else if m.Type == "reaction_added" {
|
||||||
|
// incrementUserStat(m.User, "bot-reaction-"+m.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
func (p *generalStatProcessor) ProcessBotUserMessage(m *Message) {}
|
func (p *generalStatProcessor) ProcessBotUserMessage(m *Message) {}
|
||||||
|
|
||||||
func (p *generalStatProcessor) ProcessChannelMessage(m *Message) {
|
func (p *generalStatProcessor) ProcessChannelMessage(m *Message) {
|
||||||
|
if m.Type == "message" {
|
||||||
incrementUserStat(m.User, "channel-message")
|
incrementUserStat(m.User, "channel-message")
|
||||||
|
|
||||||
incrementChannelStat(m.Channel, "message-hour-"+m.Time.Format("15"))
|
incrementChannelStat(m.Channel, "message-hour-"+m.Time.Format("15"))
|
||||||
incrementChannelStat(m.Channel, "message-dow-"+m.Time.Format("Mon"))
|
incrementChannelStat(m.Channel, "message-dow-"+m.Time.Format("Mon"))
|
||||||
incrementChannelStat(m.Channel, "message-dom-"+m.Time.Format("02"))
|
incrementChannelStat(m.Channel, "message-dom-"+m.Time.Format("02"))
|
||||||
|
//} else if m.Type == "reaction_added" {
|
||||||
|
// incrementUserStat(m.User, "channel-reaction-"+m.Name)
|
||||||
|
|
||||||
|
// incrementChannelStat(m.Channel, "reaction-"+m.Name+"-hour-"+m.Time.Format("15"))
|
||||||
|
// incrementChannelStat(m.Channel, "reaction-"+m.Name+"-dow-"+m.Time.Format("Mon"))
|
||||||
|
// incrementChannelStat(m.Channel, "reaction-"+m.Name+"-dom-"+m.Time.Format("02"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
func (p *generalStatProcessor) ProcessBotChannelMessage(m *Message) {}
|
func (p *generalStatProcessor) ProcessBotChannelMessage(m *Message) {}
|
||||||
|
|
||||||
@ -149,15 +172,27 @@ func (wm *generalWebModule) GetBottomMenuEntries() []menuItem {
|
|||||||
func (wm *generalWebModule) handleStats(w http.ResponseWriter, req *http.Request) {
|
func (wm *generalWebModule) handleStats(w http.ResponseWriter, req *http.Request) {
|
||||||
initRequest(w, req)
|
initRequest(w, req)
|
||||||
|
|
||||||
type ChannelStat struct {
|
type ChannelStats struct {
|
||||||
Name string
|
Name string
|
||||||
MemberCount int
|
MemberCount int
|
||||||
MessageCount int
|
MessageCount int
|
||||||
}
|
}
|
||||||
|
type MessageStats struct {
|
||||||
|
Hours map[int]int
|
||||||
|
Dow map[string]int
|
||||||
|
}
|
||||||
|
type UserStats struct {
|
||||||
|
Name string
|
||||||
|
Hours map[int]int
|
||||||
|
Dow map[string]int
|
||||||
|
Messages int
|
||||||
|
}
|
||||||
type StatData struct {
|
type StatData struct {
|
||||||
TotalChannelMessages int
|
TotalChannelMessages int
|
||||||
TotalChannels int
|
TotalChannels int
|
||||||
ChannelStats []ChannelStat
|
ChannelStats []ChannelStats
|
||||||
|
UserStats []UserStats
|
||||||
|
MessageStats MessageStats
|
||||||
Error string
|
Error string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,22 +202,79 @@ func (wm *generalWebModule) handleStats(w http.ResponseWriter, req *http.Request
|
|||||||
|
|
||||||
openDatabase()
|
openDatabase()
|
||||||
chanlst := getChannelList()
|
chanlst := getChannelList()
|
||||||
var chanstats []ChannelStat
|
userlst := getUserList()
|
||||||
|
var chanstats []ChannelStats
|
||||||
|
var userstats []UserStats
|
||||||
|
s.MessageStats.Hours = make(map[int]int)
|
||||||
|
for i := 0; i < 24; i++ {
|
||||||
|
s.MessageStats.Hours[i] = 0
|
||||||
|
}
|
||||||
|
s.MessageStats.Dow = make(map[string]int)
|
||||||
|
for _, k := range []string{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"} {
|
||||||
|
s.MessageStats.Dow[k] = 0
|
||||||
|
}
|
||||||
|
|
||||||
for _, k := range chanlst {
|
for _, k := range chanlst {
|
||||||
chanstats = append(chanstats, ChannelStat{
|
chanstats = append(chanstats, ChannelStats{
|
||||||
Name: getChannelName(k),
|
Name: getChannelName(k),
|
||||||
MemberCount: getChannelMemberCount(k),
|
MemberCount: getChannelMemberCount(k),
|
||||||
MessageCount: getChannelMessageCount(k),
|
MessageCount: getChannelMessageCount(k),
|
||||||
})
|
})
|
||||||
|
for i := 0; i < 24; i++ {
|
||||||
|
if hrI, err := getChannelStat(k, fmt.Sprintf("message-hour-%0d", i)); err == nil {
|
||||||
|
s.MessageStats.Hours[i] = s.MessageStats.Hours[i] + hrI
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
for _, dow := range []string{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"} {
|
||||||
|
if dowI, err := getChannelStat(k, fmt.Sprintf("message-dow-%s", dow)); err == nil {
|
||||||
|
s.MessageStats.Dow[dow] = s.MessageStats.Dow[dow] + dowI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, k := range userlst {
|
||||||
|
usrName := getUserName(k)
|
||||||
|
if usrName == "stat_bot" || usrName == "bot" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
usrHourStats := make(map[int]int)
|
||||||
|
usrDowStats := make(map[string]int)
|
||||||
|
var chanMsg int
|
||||||
|
var err error
|
||||||
|
if chanMsg, err = getUserStat(k, "channel-message"); err != nil {
|
||||||
|
chanMsg = 0
|
||||||
|
}
|
||||||
|
for i := 0; i < 24; i++ {
|
||||||
|
if hrI, err := getUserStat(k, fmt.Sprintf("message-hour-%0d", i)); err == nil {
|
||||||
|
usrHourStats[i] = hrI
|
||||||
|
} else {
|
||||||
|
usrHourStats[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, dow := range []string{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"} {
|
||||||
|
if dowI, err := getUserStat(k, fmt.Sprintf("message-dow-%s", dow)); err == nil {
|
||||||
|
usrDowStats[dow] = dowI
|
||||||
|
} else {
|
||||||
|
usrDowStats[dow] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userstats = append(userstats, UserStats{
|
||||||
|
Name: usrName,
|
||||||
|
Messages: chanMsg,
|
||||||
|
Hours: usrHourStats,
|
||||||
|
Dow: usrDowStats,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
s.ChannelStats = chanstats
|
s.ChannelStats = chanstats
|
||||||
|
s.UserStats = userstats
|
||||||
s.TotalChannelMessages = getTotalChannelMsgCount()
|
s.TotalChannelMessages = getTotalChannelMsgCount()
|
||||||
s.TotalChannels = len(chanlst)
|
s.TotalChannels = len(chanlst)
|
||||||
|
|
||||||
closeDatabase()
|
closeDatabase()
|
||||||
|
|
||||||
sc := "var stats = {totalchannelmessages:"
|
sc := "var stats = {totalchannelmessages:"
|
||||||
sc = fmt.Sprintf("%s%d,", sc, s.TotalChannelMessages)
|
sc = fmt.Sprintf("%s%d,", sc, s.TotalChannelMessages)
|
||||||
sc = sc + "\"channels\":["
|
sc = sc + "channels:["
|
||||||
for _, k := range s.ChannelStats {
|
for _, k := range s.ChannelStats {
|
||||||
sc = fmt.Sprintf("%s{name:\"%s\",member_count:%d,message_count:%d},",
|
sc = fmt.Sprintf("%s{name:\"%s\",member_count:%d,message_count:%d},",
|
||||||
sc,
|
sc,
|
||||||
@ -193,7 +285,46 @@ func (wm *generalWebModule) handleStats(w http.ResponseWriter, req *http.Request
|
|||||||
}
|
}
|
||||||
// Trim the last ,
|
// Trim the last ,
|
||||||
sc = sc[:len(sc)-1]
|
sc = sc[:len(sc)-1]
|
||||||
sc = sc + "]"
|
sc = sc + "],"
|
||||||
|
sc = sc + "users:["
|
||||||
|
for _, usr := range s.UserStats {
|
||||||
|
sc = fmt.Sprintf("%s{name:\"%s\",message_count:%d,",
|
||||||
|
sc,
|
||||||
|
usr.Name,
|
||||||
|
usr.Messages,
|
||||||
|
)
|
||||||
|
sc = sc + "messages:{"
|
||||||
|
sc = sc + "hours:{"
|
||||||
|
for i, k := range usr.Hours {
|
||||||
|
sc = sc + fmt.Sprintf("%d:%d,", i, k)
|
||||||
|
}
|
||||||
|
sc = sc[:len(sc)-1]
|
||||||
|
sc = sc + "},"
|
||||||
|
sc = sc + "dow:{"
|
||||||
|
for _, k := range []string{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"} {
|
||||||
|
sc = sc + fmt.Sprintf("%s:%d,", k, usr.Dow[k])
|
||||||
|
}
|
||||||
|
sc = sc[:len(sc)-1]
|
||||||
|
sc = sc + "}}"
|
||||||
|
sc = sc + "},"
|
||||||
|
}
|
||||||
|
// Trim the last ,
|
||||||
|
sc = sc[:len(sc)-1]
|
||||||
|
sc = sc + "],"
|
||||||
|
// Get all of the message stats
|
||||||
|
sc = sc + "messages:{"
|
||||||
|
sc = sc + "hours:{"
|
||||||
|
for i, k := range s.MessageStats.Hours {
|
||||||
|
sc = sc + fmt.Sprintf("%d:%d,", i, k)
|
||||||
|
}
|
||||||
|
sc = sc[:len(sc)-1]
|
||||||
|
sc = sc + "},"
|
||||||
|
sc = sc + "dow:{"
|
||||||
|
for _, k := range []string{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"} {
|
||||||
|
sc = sc + fmt.Sprintf("%s:%d,", k, s.MessageStats.Dow[k])
|
||||||
|
}
|
||||||
|
sc = sc[:len(sc)-1]
|
||||||
|
sc = sc + "}}"
|
||||||
sc = sc + "};"
|
sc = sc + "};"
|
||||||
|
|
||||||
addToInlineScript(sc)
|
addToInlineScript(sc)
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "net/http"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
type levelUpStatProcessor struct{}
|
type levelUpStatProcessor struct{}
|
||||||
|
|
||||||
@ -52,7 +55,7 @@ func (wm *levelUpWebModule) GetName() string {
|
|||||||
}
|
}
|
||||||
func (wm *levelUpWebModule) GetRoutes() map[string]func(http.ResponseWriter, *http.Request) {
|
func (wm *levelUpWebModule) GetRoutes() map[string]func(http.ResponseWriter, *http.Request) {
|
||||||
ret := make(map[string]func(http.ResponseWriter, *http.Request))
|
ret := make(map[string]func(http.ResponseWriter, *http.Request))
|
||||||
//ret["/levelup/"] = wm.
|
ret["/levelup/"] = wm.handleLevelUpGeneral
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
func (wm *levelUpWebModule) Register() {
|
func (wm *levelUpWebModule) Register() {
|
||||||
@ -71,6 +74,55 @@ func (wm *levelUpWebModule) GetBottomMenuEntries() []menuItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (wm *levelUpWebModule) handleLevelUpGeneral(w http.ResponseWriter, req *http.Request) {
|
func (wm *levelUpWebModule) handleLevelUpGeneral(w http.ResponseWriter, req *http.Request) {
|
||||||
initRequest(w, req)
|
type UserLevelUpStats struct {
|
||||||
|
Name string
|
||||||
|
Xp int
|
||||||
|
ChannelStats map[string]int
|
||||||
|
}
|
||||||
|
type StatData struct {
|
||||||
|
UserStats []UserLevelUpStats
|
||||||
|
}
|
||||||
|
openDatabase()
|
||||||
|
userLst := getUserList()
|
||||||
|
var userStats []UserLevelUpStats
|
||||||
|
for _, k := range userLst {
|
||||||
|
usrName := getUserName(k)
|
||||||
|
if usrName == "stat_bot" || usrName == "bot" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
xpVal, _ := getUserStat(k, "levelup-xp")
|
||||||
|
userStats = append(userStats, UserLevelUpStats{
|
||||||
|
Name: usrName,
|
||||||
|
Xp: xpVal,
|
||||||
|
ChannelStats: getAllLevelUpChannelXp(k),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
closeDatabase()
|
||||||
|
site.TemplateData = StatData{UserStats: userStats}
|
||||||
|
|
||||||
|
initRequest(w, req)
|
||||||
|
setMenuItemActive("LevelUp!")
|
||||||
|
|
||||||
|
sc := "var levelUpStats = {"
|
||||||
|
sc = sc + "users:["
|
||||||
|
for _, k := range userStats {
|
||||||
|
sc = sc + "{"
|
||||||
|
sc = sc + "name:\"" + k.Name + "\","
|
||||||
|
sc = fmt.Sprintf("%sxp:%d,", sc, k.Xp)
|
||||||
|
sc = sc + "channels:["
|
||||||
|
if len(k.ChannelStats) > 0 {
|
||||||
|
for chK, chV := range k.ChannelStats {
|
||||||
|
sc = fmt.Sprintf("%s{name:\"%s\",xp:%d},", sc, chK, chV)
|
||||||
|
}
|
||||||
|
sc = sc[:len(sc)-1]
|
||||||
|
}
|
||||||
|
sc = sc + "]},"
|
||||||
|
}
|
||||||
|
sc = sc[:len(sc)-1]
|
||||||
|
sc = sc + "]};"
|
||||||
|
addToInlineScript(sc)
|
||||||
|
site.Scripts = append(site.Scripts, "/assets/js/levelup_main.js")
|
||||||
|
|
||||||
|
showPage("levelup-main.html", site, w)
|
||||||
}
|
}
|
||||||
|
@ -87,10 +87,8 @@ func processMessage(slack *Slack, m *Message) {
|
|||||||
writeToLog(string(mb) + "\n")
|
writeToLog(string(mb) + "\n")
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// TODO: Handle reaction_added messages
|
|
||||||
// TODO: Handle reaction_removed messages
|
|
||||||
|
|
||||||
if m.Type == "message" {
|
if m.Type == "message" || m.Type == "reaction_added" {
|
||||||
var err error
|
var err error
|
||||||
var usr *User
|
var usr *User
|
||||||
|
|
||||||
|
@ -398,7 +398,6 @@ func addChannelStat(channel string, key string, addVal int) error {
|
|||||||
openDatabase()
|
openDatabase()
|
||||||
v, err := getChannelStat(channel, key)
|
v, err := getChannelStat(channel, key)
|
||||||
err = saveChannelStat(channel, key, v+addVal)
|
err = saveChannelStat(channel, key, v+addVal)
|
||||||
v, err = getChannelStat(channel, key)
|
|
||||||
closeDatabase()
|
closeDatabase()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -476,6 +475,9 @@ func getChannelStat(channel string, key string) (int, error) {
|
|||||||
ret, err = bktGetInt(chSB, key)
|
ret, err = bktGetInt(chSB, key)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
fmt.Println("Unable to find channel stats bucket: " + string(channel))
|
||||||
|
} else {
|
||||||
|
fmt.Println("Unable to find channel bucket: " + string(channel))
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
||||||
@ -828,6 +830,45 @@ func getAllUsersStats() (map[string]int, error) {
|
|||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUserList() []string {
|
||||||
|
openDatabase()
|
||||||
|
// First build channel list
|
||||||
|
var users []string
|
||||||
|
db.Update(func(tx *bolt.Tx) error {
|
||||||
|
b := tx.Bucket([]byte("users"))
|
||||||
|
return b.ForEach(func(k, v []byte) error {
|
||||||
|
users = append(users, string(k))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
})
|
||||||
|
closeDatabase()
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserName(uid string) string {
|
||||||
|
var ret string
|
||||||
|
openDatabase()
|
||||||
|
db.View(func(tx *bolt.Tx) error {
|
||||||
|
var b, uB, uIB *bolt.Bucket
|
||||||
|
var err error
|
||||||
|
|
||||||
|
b = tx.Bucket([]byte("users"))
|
||||||
|
if b == nil {
|
||||||
|
return fmt.Errorf("Error opening 'users' bucket")
|
||||||
|
}
|
||||||
|
if uB = b.Bucket([]byte(uid)); uB == nil {
|
||||||
|
return fmt.Errorf("Error opening user bucket (%s)", uid)
|
||||||
|
}
|
||||||
|
if uIB = uB.Bucket([]byte("info")); uIB == nil {
|
||||||
|
return fmt.Errorf("Error opening user info bucket (%s/info)", uid)
|
||||||
|
}
|
||||||
|
ret, err = bktGetString(uIB, "name")
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
closeDatabase()
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func getChannelMessageCount(channel string) int {
|
func getChannelMessageCount(channel string) int {
|
||||||
var ret int
|
var ret int
|
||||||
openDatabase()
|
openDatabase()
|
||||||
|
@ -71,6 +71,7 @@ func statWebMain(slack *Slack) {
|
|||||||
http.Handle("/assets/", http.StripPrefix("/assets/", assetHandler))
|
http.Handle("/assets/", http.StripPrefix("/assets/", assetHandler))
|
||||||
|
|
||||||
registerWebModule(new(generalWebModule))
|
registerWebModule(new(generalWebModule))
|
||||||
|
registerWebModule(new(levelUpWebModule))
|
||||||
|
|
||||||
http.Handle("/", r)
|
http.Handle("/", r)
|
||||||
go func() {
|
go func() {
|
||||||
@ -87,6 +88,9 @@ func initRequest(w http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
site.Scripts = make([]string, 0, 0)
|
site.Scripts = make([]string, 0, 0)
|
||||||
site.Scripts = append(site.Scripts, "/assets/js/highcharts.js")
|
site.Scripts = append(site.Scripts, "/assets/js/highcharts.js")
|
||||||
|
site.Scripts = append(site.Scripts, "/assets/js/B.js")
|
||||||
|
|
||||||
|
site.InlineScript = ""
|
||||||
|
|
||||||
site.Menu = make([]menuItem, 0, 0)
|
site.Menu = make([]menuItem, 0, 0)
|
||||||
site.BottomMenu = make([]menuItem, 0, 0)
|
site.BottomMenu = make([]menuItem, 0, 0)
|
||||||
|
8
templates/levelup-main.html
Normal file
8
templates/levelup-main.html
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<div>
|
||||||
|
<h2>Level Up!</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="btnAlphaSort" class="pure-button success">Sort by Alpha <i class="fa fa-sort-asc"></i></button>
|
||||||
|
<button id="btnXpSort" class="pure-button success">Sort by XP<i class="fa fa-sort-desc"></i> </button>
|
||||||
|
<div id="userXpList" class="pure-g">
|
||||||
|
</div>
|
@ -1,6 +1,24 @@
|
|||||||
<div>
|
<div>
|
||||||
{{ .TemplateData.Error }}
|
{{ .TemplateData.Error }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="bordered">
|
||||||
|
<button id="btnChannelStats" class="pure-button success pure-button-disabled">Channel Stats</button>
|
||||||
|
<button id="btnUserStats" class="pure-button success">User Stats</button>
|
||||||
|
<button id="btnMessageStats" class="pure-button success">Message Stats</button>
|
||||||
|
<div id="channelStats" class="bordered">
|
||||||
<div id="mainStatsBarChart"></div>
|
<div id="mainStatsBarChart"></div>
|
||||||
<hr />
|
<hr />
|
||||||
<div id="statsBarChart"></div>
|
<div id="statsBarChart"></div>
|
||||||
|
</div>
|
||||||
|
<div id="userStats" class="bordered">
|
||||||
|
<button id="btnUsrHrlyStats" class="pure-button success pure-button-disabled">Hourly</button>
|
||||||
|
<button id="btnUsrDowStats" class="pure-button success">Day of Week</button>
|
||||||
|
<div id="userHourlyStats"></div>
|
||||||
|
<div id="userDowStats"></div>
|
||||||
|
</div>
|
||||||
|
<div id="messageStats" class="bordered">
|
||||||
|
<div id="msgHourlyStats"></div>
|
||||||
|
<hr />
|
||||||
|
<div id="msgDowStats"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user