analytics: Restyle and refactor /stats page.

This completes a major redesign of the /stats page.
This commit is contained in:
Brock Whittaker 2017-06-30 12:37:59 -07:00 committed by Tim Abbott
parent dcc40c7035
commit 626c5ef55e
3 changed files with 274 additions and 450 deletions

View File

@ -3,20 +3,16 @@ var font_14pt = {
size: 14,
color: '#000000',
};
var button_selected = '#D8D8D8';
var button_unselected = '#F0F0F0';
var last_full_update = Math.min();
// TODO: should take a dict of arrays and do it for all keys
function partial_sums(array) {
var count = 0;
var cumulative = [];
for (var i = 0; i < array.length; i += 1) {
count += array[i];
cumulative[i] = count;
}
return cumulative;
var accumulator = 0;
return array.map(function (o) {
accumulator += o;
return accumulator;
});
}
// Assumes date is a round number of hours
@ -34,24 +30,17 @@ function floor_to_local_week(date) {
}
function format_date(date, include_hour) {
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var months = ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'];
var month_str = months[date.getMonth()];
var year = date.getFullYear();
var day = date.getDate();
if (include_hour) {
var hour = date.getHours();
var hour_str;
if (hour === 0) {
hour_str = '12 AM';
} else if (hour === 12) {
hour_str = '12 PM';
} else if (hour < 12) {
hour_str = hour + ' AM';
} else {
hour_str = (hour-12) + ' PM';
}
return month_str + ' ' + day + ', ' + hour_str;
var str = hour >= 12 ? "PM" : "AM";
return month_str + " " + day + ", " + (hour % 12) + ":00" + str;
}
return month_str + ' ' + day + ', ' + year;
}
@ -143,6 +132,7 @@ function populate_messages_sent_over_time(data) {
function add_hover_handler() {
document.getElementById('id_messages_sent_over_time').on('plotly_hover', function (data) {
$("#hoverinfo").show();
document.getElementById('hover_date').innerText =
data.points[0].data.text[data.points[0].pointNumber];
var values = [null, null, null];
@ -251,10 +241,7 @@ function populate_messages_sent_over_time(data) {
var clicked_cumulative = false;
function draw_or_update_plot(rangeselector, traces, last_value_is_partial, initial_draw) {
$('#daily_button').css('background', button_unselected);
$('#weekly_button').css('background', button_unselected);
$('#hourly_button').css('background', button_unselected);
$('#cumulative_button').css('background', button_unselected);
$('#daily_button, #weekly_button, #hourly_button, #cumulative_button').removeClass("selected");
if (initial_draw) {
traces.human.visible = true;
traces.bot.visible = 'legendonly';
@ -281,36 +268,36 @@ function populate_messages_sent_over_time(data) {
// Click handlers for aggregation buttons
$('#hourly_button').click(function () {
draw_or_update_plot(hourly_rangeselector, hourly_traces, false, false);
$(this).css('background', button_selected);
$(this).addClass("selected");
clicked_cumulative = false;
});
$('#daily_button').click(function () {
draw_or_update_plot(daily_rangeselector, daily_traces, last_day_is_partial, false);
$(this).css('background', button_selected);
$(this).addClass("selected");
clicked_cumulative = false;
});
$('#weekly_button').click(function () {
draw_or_update_plot(weekly_rangeselector, weekly_traces, last_week_is_partial, false);
$(this).css('background', button_selected);
$(this).addClass("selected");
clicked_cumulative = false;
});
$('#cumulative_button').click(function () {
clicked_cumulative = false;
draw_or_update_plot(daily_rangeselector, cumulative_traces, false, false);
$(this).css('background', button_selected);
$(this).addClass("selected");
clicked_cumulative = true;
});
// Initial drawing of plot
if (weekly_traces.human.x.length < 12) {
draw_or_update_plot(daily_rangeselector, daily_traces, last_day_is_partial, true);
$('#daily_button').css('background', button_selected);
$('#daily_button').addClass("selected");
} else {
draw_or_update_plot(weekly_rangeselector, weekly_traces, last_week_is_partial, true);
$('#weekly_button').css('background', button_selected);
$('#weekly_button').addClass("selected");
}
}
@ -336,8 +323,15 @@ function round_to_percentages(values, total) {
return '0%';
}
var unrounded = x/total*100;
var precision = Math.min(6, Math.max(2, Math.floor(
-Math.log(100-unrounded)/Math.log(10)) + 3));
var precision = Math.min(
6, // this is the max precision (two #, 4 decimal points; 99.9999%).
Math.max(
2, // the minimum amount of precision (40% or 6.0%).
Math.floor(-Math.log10(100 - unrounded)) + 3
)
);
return unrounded.toPrecision(precision) + '%';
});
}
@ -462,23 +456,18 @@ function populate_messages_sent_by_client(data) {
var time_button;
if (data.end_times.length >= 30) {
time_button = 'month';
$('#messages_by_client_last_month_button').css('background', button_selected);
$('#messages_by_client_last_month_button').addClass("selected");
} else {
time_button = 'cumulative';
$('#messages_by_client_cumulative_button').css('background', button_selected);
}
function remove_button(button_id) {
var elem = document.getElementById(button_id);
elem.parentNode.removeChild(elem);
$('#messages_by_client_cumulative_button').addClass("selected");
}
if (data.end_times.length < 365) {
remove_button('messages_by_client_last_year_button');
$("#pie_messages_sent_by_client button[data-time='year']").remove();
if (data.end_times.length < 30) {
remove_button('messages_by_client_last_month_button');
$("#pie_messages_sent_by_client button[data-time='month']").remove();
if (data.end_times.length < 7) {
remove_button('messages_by_client_last_week_button');
$("#pie_messages_sent_by_client button[data-time='week']").remove();
}
}
}
@ -497,52 +486,24 @@ function populate_messages_sent_by_client(data) {
// Click handlers
function set_user_button(button) {
$('#messages_by_client_realm_button').css('background', button_unselected);
$('#messages_by_client_user_button').css('background', button_unselected);
button.css('background', button_selected);
$("#pie_messages_sent_by_client button[data-user]").removeClass("selected");
button.addClass("selected");
}
function set_time_button(button) {
$('#messages_by_client_cumulative_button').css('background', button_unselected);
$('#messages_by_client_last_year_button').css('background', button_unselected);
$('#messages_by_client_last_month_button').css('background', button_unselected);
$('#messages_by_client_last_week_button').css('background', button_unselected);
button.css('background', button_selected);
$("#pie_messages_sent_by_client button[data-time]").removeClass("selected");
button.addClass("selected");
}
$('#messages_by_client_realm_button').click(function () {
set_user_button($(this));
user_button = 'realm';
draw_plot();
});
$('#messages_by_client_user_button').click(function () {
set_user_button($(this));
user_button = 'user';
draw_plot();
});
$('#messages_by_client_cumulative_button').click(function () {
set_time_button($(this));
time_button = 'cumulative';
draw_plot();
});
$('#messages_by_client_last_year_button').click(function () {
set_time_button($(this));
time_button = 'year';
draw_plot();
});
$('#messages_by_client_last_month_button').click(function () {
set_time_button($(this));
time_button = 'month';
draw_plot();
});
$('#messages_by_client_last_week_button').click(function () {
set_time_button($(this));
time_button = 'week';
$("#pie_messages_sent_by_client button").click(function () {
if ($(this).attr("data-user")) {
set_user_button($(this));
user_button = $(this).attr("data-user");
}
if ($(this).attr("data-time")) {
set_time_button($(this));
time_button = $(this).attr("data-time");
}
draw_plot();
});
@ -578,7 +539,7 @@ $.get({
function populate_messages_sent_by_message_type(data) {
var layout = {
margin: { l: 90, r: 0, b: 0, t: 0 },
width: 550,
width: 750,
height: 300,
font: font_14pt,
};
@ -605,7 +566,7 @@ function populate_messages_sent_by_message_type(data) {
colors: ['#68537c', '#be6d68', '#b3b348'],
},
},
total_str: "Total messages: " + plot_data.total.toString().
total_str: "<b>Total messages:</b> " + plot_data.total.toString().
replace(/\B(?=(\d{3})+(?!\d))/g, ","),
};
}
@ -629,24 +590,19 @@ function populate_messages_sent_by_message_type(data) {
var time_button;
if (data.end_times.length >= 30) {
time_button = 'month';
$('#messages_by_type_last_month_button').css('background', button_selected);
$('#messages_by_type_last_month_button').addClass("selected");
} else {
time_button = 'cumulative';
$('#messages_by_type_cumulative_button').css('background', button_selected);
$('#messages_by_type_cumulative_button').addClass("selected");
}
var totaldiv = document.getElementById('pie_messages_sent_by_type_total');
function remove_button(button_id) {
var elem = document.getElementById(button_id);
elem.parentNode.removeChild(elem);
}
if (data.end_times.length < 365) {
remove_button('messages_by_type_last_year_button');
$("#pie_messages_sent_by_type button[data-time='year']").remove();
if (data.end_times.length < 30) {
remove_button('messages_by_type_last_month_button');
$("#pie_messages_sent_by_type button[data-time='month']").remove();
if (data.end_times.length < 7) {
remove_button('messages_by_type_last_week_button');
$("#pie_messages_sent_by_type button[data-time='week']").remove();
}
}
}
@ -663,52 +619,24 @@ function populate_messages_sent_by_message_type(data) {
// Click handlers
function set_user_button(button) {
$('#messages_by_type_realm_button').css('background', button_unselected);
$('#messages_by_type_user_button').css('background', button_unselected);
button.css('background', button_selected);
$("#pie_messages_sent_by_type button[data-user]").removeClass("selected");
button.addClass("selected");
}
function set_time_button(button) {
$('#messages_by_type_cumulative_button').css('background', button_unselected);
$('#messages_by_type_last_year_button').css('background', button_unselected);
$('#messages_by_type_last_month_button').css('background', button_unselected);
$('#messages_by_type_last_week_button').css('background', button_unselected);
button.css('background', button_selected);
$("#pie_messages_sent_by_type button[data-time]").removeClass("selected");
button.addClass("selected");
}
$('#messages_by_type_realm_button').click(function () {
set_user_button($(this));
user_button = 'realm';
draw_plot();
});
$('#messages_by_type_user_button').click(function () {
set_user_button($(this));
user_button = 'user';
draw_plot();
});
$('#messages_by_type_cumulative_button').click(function () {
set_time_button($(this));
time_button = 'cumulative';
draw_plot();
});
$('#messages_by_type_last_year_button').click(function () {
set_time_button($(this));
time_button = 'year';
draw_plot();
});
$('#messages_by_type_last_month_button').click(function () {
set_time_button($(this));
time_button = 'month';
draw_plot();
});
$('#messages_by_type_last_week_button').click(function () {
set_time_button($(this));
time_button = 'week';
$("#pie_messages_sent_by_type button").click(function () {
if ($(this).attr("data-user")) {
set_user_button($(this));
user_button = $(this).attr("data-user");
}
if ($(this).attr("data-time")) {
set_time_button($(this));
time_button = $(this).attr("data-time");
}
draw_plot();
});
}
@ -737,7 +665,7 @@ function populate_number_of_users(data) {
fixedrange: true,
rangeselector: {
x: 0.808,
y: -0.2,
y: -0.33,
buttons: [
{
count: 30,
@ -763,9 +691,7 @@ function populate_number_of_users(data) {
return new Date(timestamp*1000);
});
var text = end_dates.map(function (date) {
return format_date(date, false);
});
var text = end_dates.map(format_date);
var trace = {
x: end_dates,
@ -780,10 +706,10 @@ function populate_number_of_users(data) {
Plotly.newPlot('id_number_of_users', [trace], layout, {displayModeBar: false});
document.getElementById('id_number_of_users').on('plotly_hover', function (data) {
document.getElementById('users_hover_date').innerText =
data.points[0].data.text[data.points[0].pointNumber];
document.getElementById('users_hover_humans').style.display = 'inline';
document.getElementById('users_hover_humans_value').innerText = data.points[0].y;
$("#users_hover_info").show();
$("#users_hover_date").text(data.points[0].data.text[data.points[0].pointNumber]);
$("#users_hover_humans").css("display", "inline");
$("#users_hover_humans_value").text(data.points[0].y);
});
}

View File

@ -6,90 +6,112 @@
body {
font-family: 'Humbug', 'Helvetica Neue', sans-serif !important;
background-color: #fafafa;
-webkit-font-smoothing: antialiased;
}
p {
margin-bottom: 0px;
}
svg {
-webkit-font-smoothing: antialiased;
.svg-container {
margin: 20px;
}
.center-charts {
margin: 30px auto;
width: 790px; /* chart = 750px + 20px padding */
}
.chart-container {
display: inline-block;
vertical-align: top;
margin: 10px 0px;
padding: 20px;
border: 2px solid #eee;
background-color: #fff;
}
.analytics-page-header {
margin-left: 10px;
}
hr {
border-width: 2px;
}
.rangeselector text {
font-weight: 400;
}
.pie-chart .number-stat {
float: left;
font-size: 0.8em;
font-weight: 500;
}
.chart-container:not(.pie-chart) .number-stat {
position: relative;
top: -30px;
}
.buttons button {
background-color: #f0f0f0;
}
.buttons button.selected {
background-color: #d8d8d8;
}
.pie-chart .buttons {
float: right;
}
.pie-chart .buttons button {
padding: 2px 6px;
}
.chart-container.pie-chart hr {
margin-bottom: 8px;
}
.chart-container button {
position: relative;
z-index: 1;
}
.chart-container h1 {
margin-top: 0px;
font-size: 1.5em;
}
.chart-container .button-container {
position: relative;
z-index: 1;
}
.chart-container .button-container label {
margin: 3px;
}
.chart-container .button-container > * {
display: inline-block;
vertical-align: top;
}
.alert {
display: none;
}
.analytics-page-header {
margin-top: 30px;
margin-bottom: 50px;
text-align: center;
font-size: 36px;
}
.page-content {
width: 100%;
overflow: auto;
display: inline-block;
vertical-align: top;
}
.center-container {
min-height: 0px;
}
.graph-title {
text-align: center;
font-size: 20px;
margin-bottom: 30px;
}
#graph_container {
position: relative;
}
#button_label {
font-size: 15px;
margin-left: 40px;
margin-bottom: 0px;
}
#hoverinfo {
position: relative;
top: -33px;
height: 0;
width: 400px;
z-index: 1;
font-size: 14px;
margin-left: 40px;
}
#id_messages_sent_over_time {
user-select: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
#id_messages_sent_over_time[last_value_is_partial='true'] .points path:last-of-type {
opacity: 0.5;
}
#hover_date {
font-weight: 400;
}
#hover_human {
font-weight: 400;
margin-left: 10px;
display: none;
}
#hover_me,
#hover_bot {
font-weight: 400;
margin-left: 8px;
#users_hover_info,
#hoverinfo {
display: none;
}
@ -104,181 +126,8 @@ svg {
background: hsl(0, 0%, 84%) !important;
}
.pie-button {
font-family: 'Humbug', 'Helvetica Neue', sans-serif !important;
font-weight: inherit;
border: none;
border-radius: 4px;
padding: 0px 5px 0px 5px;
margin: 5px 0px 0px 0px;
}
.pie-button:hover {
background: hsl(0, 0%, 84%) !important;
}
#button_container {
position: absolute;
top: -12px;
z-index: 1;
}
#hourly_button {
padding: 0px 5px 0px 5px;
font-size: 14px;
margin-left: 40px;
margin-top: 0px;
background: hsl(0, 0%, 94%);
}
#daily_button {
padding: 0px 5px 0px 5px;
font-size: 14px;
margin-top: 0px;
background: hsl(0, 0%, 94%);
}
#weekly_button {
padding: 0px 5px 0px 5px;
font-size: 14px;
margin-top: 0px;
background: hsl(0, 0%, 94%);
}
#cumulative_button {
padding: 0px 5px 0px 5px;
font-size: 14px;
margin-top: 0px;
background: hsl(0, 0%, 94%);
}
#messages_by_client_realm_button {
font-size: 14px;
background: hsl(0, 0%, 84%);
}
#messages_by_client_user_button {
font-size: 14px;
background: hsl(0, 0%, 94%);
}
#messages_by_client_cumulative_button {
font-size: 14px;
background: hsl(0, 0%, 94%);
}
#messages_by_client_last_year_button {
font-size: 14px;
background: hsl(0, 0%, 94%);
}
#messages_by_client_last_month_button {
font-size: 14px;
background: hsl(0, 0%, 94%);
}
#messages_by_client_last_week_button {
font-size: 14px;
margin-left: 50px;
background: hsl(0, 0%, 94%);
}
#messages_by_type_realm_button {
font-size: 14px;
margin-bottom: 100px;
background: hsl(0, 0%, 84%);
}
#messages_by_type_user_button {
font-size: 14px;
background: hsl(0, 0%, 94%);
margin-bottom: 100px;
}
#messages_by_type_cumulative_button {
font-size: 14px;
background: hsl(0, 0%, 94%);
vertical-align: top;
}
#messages_by_type_last_year_button {
font-size: 14px;
background: hsl(0, 0%, 94%);
vertical-align: top;
}
#messages_by_type_last_month_button {
font-size: 14px;
background: hsl(0, 0%, 94%);
vertical-align: top;
}
#messages_by_type_last_week_button {
font-size: 14px;
background: hsl(0, 0%, 94%);
vertical-align: top;
margin-left: 50px;
}
#pie_container {
text-align: center;
}
#pie_messages_sent_by_client {
display: inline-block;
vertical-align: top;
margin: 100px 40px 0px 0px;
}
#pie_messages_sent_by_type {
display: inline-block;
vertical-align: top;
margin: 100px 0px 0px 0px;
}
#pie_messages_sent_by_client_total {
font-size: 14px;
text-align: left;
margin: 0px 0px 5px 45px;
}
#pie_messages_sent_by_type_total {
font-size: 14px;
text-align: left;
margin: 0px 0px 5px 53px;
}
#users_hover_info {
position: relative;
top: -65px;
height: 0;
width: 400px;
z-index: 1;
font-size: 14px;
margin-left: 20px;
}
#users_hover_date {
font-weight: 400;
margin-left: 18px;
}
#users_hover_humans {
font-weight: 400;
margin-left: 18px;
display: none;
}
.last-update {
display: none;
color: hsl(0, 0%, 66%);
padding: 50px 0px;
font-weight: bolder;
font-size: 16px;
}
.last-update .icon-vector-question-sign {
margin-left: 5px;
.button[data-user="realm"] {
margin-right: 10px;
}
.tooltip-inner {
@ -291,9 +140,49 @@ svg {
line-height: 1.4;
}
@media (max-width: 1127px) {
#pie_messages_sent_by_client {
margin-right: 0px;
margin-bottom: 40px;
.last-update {
margin: 0px 0px 30px 0px;
font-size: 0.8em;
font-weight: 400;
text-align: center;
color: #aaa;
}
#users_hover_info,
#hoverinfo {
font-size: 0.8em;
font-weight: 500;
}
#users_hover_humans,
#hover_human {
margin-left: 10px;
}
#hoverinfo,
#users_hover_info {
position: relative;
height: 0px;
top: -35px;
left: 40px;
}
#users_hover_info {
left: 25px;
top: -40px;
}
@media (min-width: 1680px) {
.center-charts {
width: calc(816px * 2); /* 790px + 4px for borders + 2px for il-block + 20px margins */
}
.center-charts .left,
.center-charts .right {
display: inline-block;
vertical-align: top;
margin: 0px 10px;
width: 790px;
}
}

View File

@ -12,74 +12,83 @@
<div class="page-content">
<div id="id_stats_errors" class="alert alert-error"></div>
<h1 class="analytics-page-header">Zulip Analytics for {{ realm_name }}</h1>
<div class="center-container">
<div class="center-block">
<p class="graph-title" id="messages_timescale_anchor">Messages Sent Over Time</p>
<div id="graph_container">
<div id="button_container">
<p id="button_label"> Aggregation </p>
<button class="button" type="button" id='hourly_button'> Hourly </button>
<button class="button" type="button" id='daily_button'> Daily </button>
<button class="button" type="button" id='weekly_button'> Weekly </button>
<button class="button" type="button" id='cumulative_button'> Cumulative </button>
<div class="center-charts">
<h1 class="analytics-page-header">Zulip Analytics for {{ realm_name }}</h1>
<div class="left">
<div class="chart-container">
<h1>Messages Sent Over Time</h1>
<div class="button-container">
<label>Aggregation</label>
<div class="buttons">
<button class="button" type="button" id='hourly_button'>Hourly </button>
<button class="button" type="button" id='daily_button'>Daily </button>
<button class="button" type="button" id='weekly_button'>Weekly </button>
<button class="button" type="button" id='cumulative_button'>Cumulative </button>
</div>
</div>
<div id="id_messages_sent_over_time"></div>
<div id="hoverinfo">
<span id="hover_date"></span>
<span id="hover_me">Me:</span>
<span id="hover_me_value"></span>
<span id="hover_human">Humans:</span>
<b id="hover_human">Humans:</b>
<span id="hover_human_value"></span>
<span id="hover_bot">Bots:</span>
<b id="hover_bot">Bots:</b>
<span id="hover_bot_value"></span>
</div>
</div>
<div class="chart-container pie-chart">
<div id="pie_messages_sent_by_client">
<h1>Messages Sent by Client</h1>
<div id="id_messages_sent_by_client" class="number-stat"></div>
<div class="buttons">
<button class="button" type="button" data-user="user">Me</button>
<button class="button selected" type="button" data-user="realm">Everyone</button>
<button class="button" type="button" data-time="week">Last Week</button>
<button class="button selected" type="button" data-time="month">Last Month</button>
<button class="button" type="button" data-time="year">Last Year</button>
<button class="button" type="button" data-time="cumulative">All Time</button>
</div>
</div>
</div>
</div>
<div class="right">
<div class="chart-container pie-chart">
<div id="pie_messages_sent_by_type">
<h1>Messages Sent by Recipient Type</h1>
<div id="id_messages_sent_by_message_type"></div>
<div id="pie_messages_sent_by_type_total" class="number-stat"></div>
<div class="buttons">
<button class="button" type="button" data-user="user">Me</button>
<button class="button selected" type="button" data-user="realm">Everyone</button>
<button class="button" type="button" data-time="week">Last Week</button>
<button class="button selected" type="button" data-time="month">Last Month</button>
<button class="button" type="button" data-time="year">Last Year</button>
<button class="button" type="button" data-time="cumulative">All Time</button>
</div>
</div>
</div>
<div class="chart-container">
<h1>Active Users</h1>
<div id="id_number_of_users"></div>
<div id="users_hover_info" class="number-stat">
<span id="users_hover_date"></span>
<b id="users_hover_humans">Users: </b>
<span id="users_hover_humans_value"></span>
</div>
</div>
</div>
</div>
<div id="pie_container">
<div id="pie_messages_sent_by_client">
<p class="graph-title" id="messages_by_client_anchor">Messages Sent by Client</p>
<div id="id_messages_sent_by_client"></div>
<button class="pie-button" type="button" id='messages_by_client_user_button'> Me </button>
<button class="pie-button" type="button" id='messages_by_client_realm_button'> Everyone </button>
<button class="pie-button" type="button" id='messages_by_client_last_week_button'> Last Week </button>
<button class="pie-button" type="button" id='messages_by_client_last_month_button'> Last Month </button>
<button class="pie-button" type="button" id='messages_by_client_last_year_button'> Last Year </button>
<button class="pie-button" type="button" id='messages_by_client_cumulative_button'> All Time </button>
</div>
<div id="pie_messages_sent_by_type">
<p class="graph-title" id="messages_by_type_anchor">Messages Sent by Recipient Type</p>
<div id="id_messages_sent_by_message_type"></div>
<div id="pie_messages_sent_by_type_total"> </div>
<button class="pie-button" type="button" id='messages_by_type_user_button'> Me </button>
<button class="pie-button" type="button" id='messages_by_type_realm_button'> Everyone </button>
<button class="pie-button" type="button" id='messages_by_type_last_week_button'> Last Week </button>
<button class="pie-button" type="button" id='messages_by_type_last_month_button'> Last Month </button>
<button class="pie-button" type="button" id='messages_by_type_last_year_button'> Last Year </button>
<button class="pie-button" type="button" id='messages_by_type_cumulative_button'> All Time </button>
</div>
</div>
<div class="center-container">
<div class="center-block">
<p class="graph-title" id="users_anchor">Active Users</p>
<div id="id_number_of_users"></div>
<div id="users_hover_info">
<span id="users_hover_date"></span>
<span id="users_hover_humans">Users:</span>
<span id="users_hover_humans_value"></span>
</div>
</div>
</div>
<div class="center-container">
<div class="center-block">
<p class="last-update">
Last Update: <span id="id_last_full_update"></span>
<span data-toggle="tooltip" title="{{ _('A full update of all the graphs happens once a day.<br/> The “Messages Sent Over Time” graph is updated once an hour.') }}">
<span class="icon-vector-question-sign" id="id_last_update_question_sign"></span>
</span>
</p>
</div>
<div class="last-update">
Last Update: <span id="id_last_full_update"></span>
<span data-toggle="tooltip" title="{{ _('A full update of all the graphs happens once a day.<br/>The “Messages Sent Over Time” graph is updated once an hour.') }}">
<span class="icon-vector-question-sign" id="id_last_update_question_sign"></span>
</span>
</div>
</div>