blob: 69f9e7af58c85b16309d377fc744a213d448e981 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// showTrends: Show the ML trends on trends.html
function showTrends(json, state) {
var now = new Date().getTime() / 1000
// Do we have a trend DOM object to edit?
var obj = document.getElementById('trends')
if (!obj) {
return;
}
// size down trend obj
obj.style.maxWidth = "660px"
// Make sure we actually have a timespan > 0 days to analyze.
if (state.tspan == 0) {
obj.innerHTML += "<h4>Invalid date range specified!</h4>"
return
}
// Add the timespan if it makes sense (has a beginning and end)
var daterange = ""
if (state.dfrom || state.dto) {
daterange = " between " + (state.dfrom ? state.dfrom.toDateString() : "beginning of time") + " and " + (state.dto ? state.dto.toDateString() : "now")
}
// Link back to list view if possible
var lname = json.list;
if (lname.search(/\*/) == -1) {
lname = "<a href='list.html?" + lname + "'>" + lname + "</a>"
}
// Set page title
var title = "<div><h2>Statistics for " + lname + "<br/><small>" + daterange + ":</small></h2>"
if ((state.query && state.query.length > 0) || (state.nquery && state.nquery.length > 0)) {
title += "<i>(NB: You are using a search query which may distort these results)"
}
title += "</div>"
obj.innerHTML = title
// for sake of displaying "N days" or just "days", make tspan empty string if null
if (state.tspan == null) {
state.tspan = ""
}
// save each daily stat for later canvas drawing
var daily = {}
// total emails sent in the past N days
var total_emails_current = 0;
var total_emails_past = 0;
// For each email, count the ones in this and the previous time span
for (var i in json.emails) {
var f = parseInt(json.emails[i].epoch/86400)
daily[f] = daily[f] ? daily[f]+1 : 1
if ((state.dfrom == null) || json.emails[i].epoch >= (state.dfrom.getTime()/1000)) {
total_emails_current++;
} else {
total_emails_past++;
}
}
// change since past timespan as relative number and percentage
var diff = total_emails_current-total_emails_past
var pct = parseInt((diff / total_emails_past)*100)
// Make div for emails sent
var emls_sent = document.createElement('div')
emls_sent.setAttribute("style", "float: left; margin: 10px; padding: 5px; text-align: left; border-radius: 8px; background: #F8684E; color: #FFF; font-family: sans-serif; width: 300px;")
emls_sent.innerHTML = "<h2 style='margin: 0px; padding: 0px; text-align: left;'><span class='glyphicon glyphicon-envelope'> </span> " + total_emails_current.toLocaleString() + "</h2><span style='font-size: 13px;'>Emails sent during these " + state.tspan + " days,<br/></span>"
// If a comparison with previous timespan makes sense (can be calculated), show it
if (!isNaN(pct)) {
if (total_emails_current >= total_emails_past) {
emls_sent.innerHTML += "<span style='font-size: 11px;'><b style='color:#00D0F1'>up</b> " + (total_emails_current-total_emails_past) + " (" + pct + "%) compared to previous " + state.tspan + " days.</span>"
} else {
emls_sent.innerHTML += "<span style='font-size: 11px;'><b style='color:#F9BA00'>down</b> " + (total_emails_past-total_emails_current) + " (" + pct + "%) compared to previous " + state.tspan + " days.</span>"
}
}
obj.appendChild(emls_sent)
// total topics started in the past 3 months
var total_topics_current = 0;
var total_topics_past = 0;
// For each thread, count the ones _started_ in this time span and the previous one
for (var i in json.thread_struct) {
if ((state.dfrom == null) || json.thread_struct[i].epoch >= (state.dfrom.getTime()/1000)) {
total_topics_current++;
} else {
total_topics_past++;
}
}
var diff = total_topics_current-total_topics_past
var pct = parseInt((diff / total_topics_past)*100)
// Make div for topics started
var topics_sent = document.createElement('div')
topics_sent.setAttribute("style", "float: left; margin: 10px; padding: 5px; text-align: left; border-radius: 8px; background: #F99A00; color: #FFF; font-family: sans-serif; width: 300px;")
topics_sent.innerHTML = "<h2 style='margin: 0px; padding: 0px; text-align: left;'><span class='glyphicon glyphicon-list-alt'> </span> " + total_topics_current.toLocaleString() + "</h2><span style='font-size: 13px;'>topics started during these " + state.tspan + " days,<br/></span>"
// If a comparison with previous timespan makes sense (can be calculated), show it
if (!isNaN(pct)) {
if (total_topics_current >= total_topics_past) {
topics_sent.innerHTML += "<span style='font-size: 11px;'><b style='color:#00D0F1'>up</b> " + (total_topics_current-total_topics_past) + " (" + pct + "%) compared to previous " + state.tspan + " days.</span>"
} else {
topics_sent.innerHTML += "<span style='font-size: 11px;'><b style='color:#F9BA00'>down</b> " + (total_topics_past-total_topics_current) + " (" + pct + "%) compared to previous " + state.tspan + " days.</span>"
}
}
obj.appendChild(topics_sent)
// people participating in the past 3 months
// As we can't just count them, we'll construct a hash and count the no. of elements in it
var total_people_current = 0;
var total_people_past = 0;
var hc = {}
var hp = {}
// For each email, add to the sender hash for current and previous time span. Count 'em later
for (var i in json.emails) {
if ((state.dfrom == null) || json.emails[i].epoch >= (state.dfrom.getTime()/1000)) {
hc[json.emails[i].from] = (hc[json.emails[i].from] ? hc[json.emails[i].from] : 0) + 1
} else {
hp[json.emails[i].from] = (hp[json.emails[i].from] ? hp[json.emails[i].from] : 0) + 1
}
}
// count elements in the hashes
for (var i in hc) { total_people_current++;}
for (var i in hp) { total_people_past++;}
var diff = total_people_current-total_people_past
var pct = parseInt((diff / total_people_past)*100)
// Make div for participants
var parts = document.createElement('div')
parts.setAttribute("style", "float: left; break-after: always; margin: 10px; padding: 5px; text-align: left; border-radius: 8px; background: #00A757; color: #FFF; font-family: sans-serif; width: 300px;")
parts.innerHTML = "<h2 style='margin: 0px; padding: 0px; text-align: left;'><span class='glyphicon glyphicon-user'> </span> " + total_people_current.toLocaleString() + "</h2><span style='font-size: 13px;'>Participants during these " + state.tspan + " days,</span><br/>"
// If a comparison with previous timespan makes sense (can be calculated), show it
if (!isNaN(pct)) {
if (total_people_current >= total_people_past) {
parts.innerHTML += "<span style='font-size: 11px;'><b style='color:#00D0F1'>up</b> " + (total_people_current-total_people_past) + " (" + pct + "%) compared to previous " + state.tspan + " days.</span>"
} else {
parts.innerHTML += "<span style='font-size: 11px;'><b style='color:#F9BA00'>down</b> " + (total_people_past-total_people_current) + " (" + pct + "%) compared to previous " + state.tspan + " days.</span>"
}
}
obj.appendChild(parts)
// Display charts if possible
if (state.dfrom && state.dto) {
if (!pm_config.trendPie) {
document.getElementById('trendCanvas').setAttribute("height", "340")
document.getElementById('top10pie').setAttribute("height", "0")
}
quokkaBars("trendCanvas",
['Previous timespan', 'Current timespan'],
[
["Emails sent", total_emails_past, total_emails_current],
["Topics started", total_topics_past, total_topics_current],
["Participants", total_people_past, total_people_current],
],
{ stack: false, curve: false, title: "Stats for the past " + state.tspan + " days (compared to previous timespan)", nox: false }
);
}
GetAsync('/api/stats.lua?list='+state.listname+'&domain='+state.domain+'&d=' + state.dspan + "&q=" + ((state.query && state.query.length > 0) ? state.query : "") + state.nquery, {tspan: state.tspan}, showTop)
// daily chart rendering with quokka
var days = []
for (var d in daily) {
days.push(d)
}
days.sort()
var arr = []
// Start from the beginning
var D = new Date(state.dfrom)
D.setDate(D.getDate()-state.tspan)
// For each day from $beginning to $now, push the no. of emails sent that day into an array
while (D <= state.dto) {
var day = new Date(D)
D.setDate(D.getDate()+1)
var d = parseInt(D.getTime()/86400/1000) // make correct pointer to daily[] array
// if in this timespan, color it blue
if (day.getTime() >= state.dfrom.getTime()) {
arr.push([day, daily[d] ? daily[d] : 0, '#00C0F1'])
// else, color it green
} else {
arr.push([day, daily[d] ? daily[d] : 0, '#2DC47B'])
}
}
// draw the chart
quokkaBars("dayCanvas", ['Current timespan', '', 'Previous timespan'], arr, {verts: false, title: "Daily email stats"})
// Add ngrams teaser
var obj = document.getElementById('ngrams')
obj.innerHTML = "Interested in more data? Try our <a href='ngrams.html?" + document.location.search.substr(1) + "'> n-grams page</a>!"
}
// callback for top10 stats
function showTop(json, state) {
// Make sure we have a trend object to edit in the DOM
var obj = document.getElementById('trends')
if (!obj) {
return;
}
var daterange = ""
// Can't do much analysis if the timespan is 0 days
if (state.tspan == 0) {
return
}
// Top 10 participants
var top10 = document.createElement('div')
top10.setAttribute("style", "float: left; margin: 10px; padding: 5px; text-align: left; border-radius: 8px; background: #00C0F1; color: #FFF; font-family: sans-serif; width: 300px; min-height: 300px;")
top10.innerHTML = "<h3 style='margin: 0px; padding: 0px; text-align: left;'><span class='glyphicon glyphicon-star-empty'> </span> Top 10 participants:</h3>"
var l = "<ul style='margin-left: 0px; padding-left: 0px; list-style: none;'>"
var ph = []
var max = 0
for (var i in json.participants) {
var part = json.participants[i]
ph.push({title: part.name, value: part.count})
max += part.count
l += "<li style='font-size: 13px;'><img src='https://secure.gravatar.com/avatar/" + part.gravatar + ".jpg?s=24&r=g&d=mm' style='margin-top: 3px; margin-right: 5px;'/><b>" + part.name.replace(/</, "&lt;") + ": </b>" + part.count + " email" + (part.count == 1 ? "" : "s") + "</li>"
}
l += "</ul>"
top10.innerHTML += l
ph.push({title: 'Others', value: json.hits - max})
obj.insertBefore(top10, obj.childNodes[1])
if (pm_config.trendPie) {
quokkaCircle("top10pie", ph);
}
}
// onload func that figures out what we want and then asks the API for stats
// invoked by onload in trends.html
function gatherTrends() {
// get list, timespan and query from the html page
var args = document.location.search.substr(1)
var a_arr = args.split(/:/, 3)
var list = a_arr[0]
var dspan = a_arr[1]
var query = a_arr[2]
if (!valid_address(list)) {
alert("Invalid mailing list address supplied!");
return
}
// Try to detect header searches, if present
var nquery = ""
if (query && query.length > 0) {
var stuff = ['from', 'subject', 'body']
for (var k in stuff) {
// can we find 'header=foo' stuff?
var r = RegExp(stuff[k] + "=(.+)", "mi")
var m = query.match(r)
if (m) {
query = query.replace(m[0], "")
// append to the header_foo query
nquery += "&header_" + stuff[k] + "=" + encodeURIComponent(m[1])
}
}
}
// don't let JavaScript try to send 'undefined' as an actual query here.
if (query == undefined) {
query = ""
}
// default to 1 month view if nothing else is supplied
if (!dspan || dspan.length == 0) {
dspan = DEFAULT_RETENTION
}
// figure out when this is and what the double is (for comparisons)
var xa = datePickerDouble(dspan)
// split list name for stats.lua
var arr = list.split(/@/)
var listname = arr[0]
var domain = arr[1]
// Get us some data
GetAsync('/api/stats.lua?list='+listname+'&domain='+domain+'&d=' + xa[0] + "&q=" + ((query && query.length > 0) ? encodeURIComponent(query) : "") + nquery, { nquery: nquery, listname: listname, domain: domain, dbl: xa[0], dfrom: xa[1], dto: xa[2], tspan: xa[3], dspan: dspan, query: query }, showTrends)
document.title = "Stats for " + list + " - Pony Mail!"
}